【JavaScript】ブラウザで緯度経度の情報を取得する

apiを使わずに、navigator.geolocation.getCurrentPositionだけでJSで取得でくる。
ただし、chromeではSSL環境では使用できない。そのため、テストをする際もローカルではデフォルトではテストできないので、SSL環境を構築する必要がある。※動作確認するために、ドメインを取得してLet’s Encryptの環境を作りました。

<head>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
</head>

<div id="app">
    <h1>現在地</h1>
    <span v-if="err">
        緯度経度の情報を取得できませんでした。
    </span>
    <ul>
        <li>緯度:{{lat}}</li>
        <li>経度:{{long}}</li>
    </ul>
    <a v-bind:href="`${url}`">登録する</a>
</div>

<script>
let lat = "";
let long = "";
function success(pos) {
  const crd = pos.coords;

  lat = crd.latitude;
  long = crd.longitude;
 
  let url = "/index.html?lat=" +lat + "&long=" + long;
  var app = new Vue({
    el: '#app',
    data: {
        lat: lat,
        long: long,
        url: url,
        err: null
    }
  })
}

function error(err) {
    console.warn(`ERROR(${err.code}): ${err.message}`);
    let url = "/index.html?lat=" +lat + "&long=" + long;
    var app = new Vue({
    el: '#app',
    data: {
        lat: lat,
        long: long,
        url: url,
        err: err
    }
  })
}
navigator.geolocation.getCurrentPosition(success, error);
</script>

Next.js 入門

$ npm install -g pnpm
$ pnpm -v
10.6.5
$ npx create-next-app@latest nextjs-blog –use-pnpm
$ cd nextjs-blog
$ pnpm run dev

app/page.tsx

import Image from "next/image";

export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <h1 className="text-4xl font-bold">Welcome!</h1>
      <p className="text-xl">This is where our journey begins!</p>
    </main>
  );
}
[/code

$ mkdir -p app/about
$ touch app/about/page.tsx

export default function About() {
    return (
      <div className="p-24">
        <h1 className="text-2xl font-bold mb-4">About My Blog</h1>
        <p>This blog app is designed to share insights and knowledge.</p>
      </div>
    );
  }

http://192.168.33.10:3000/about

appディレクトリで各ファイルが対応するURLに自動的にマッピングされる
publicは静的ファイル
next.config.jsはNext.jsの設定をカスタマイズ

export const posts = [
    {id: '1', title: '最初の投稿', content: 'これは最初の投稿です', author: 'Alice', createdAt: new Date('2025-01-01')},
    {id: '2', title: '2番目の投稿', content: 'これは2番目の投稿です', author: 'Bob', createdAt: new Date('2025-01-02')},
]
import Link from 'next/link';
import {posts} from '@/app/lib/placeholder-data';

export default function BlogList() {
    return (
        <div className="container mx-auto px-4 py-8">
            <h1 className="text-3xl font-bold mb-4">ブログ投稿一覧</h1>
            <ul className="space-y-4">
                {posts.map((post) => (
                    <li key={post.id} className="border p-4 rounded-lg">
                        <Link href={`/blog/${post.id}`} className="text-xl font-semibold text-blue-600 hover:underline">
                            {post.title}
                        </Link>
                        <p className="text-gray-600">{post.author} - {post.createdAt.toLocaleDateString()}</p>    
                    </li>
                ))}
            </ul>

        </div>
    );
}

なるほど、世界観は少しだけ理解しました^^

DEXとは何か? Uniswap/Raydiumを触ってみる

DEXはDecentralized Exchangesの略称で、仲介者となる企業が存在しない、ユーザ同士が直接仮想通貨を取引できるブロックチェーン上に構築される取引所

### 代表的なDEX
– Uniswap (ECR-20規格であれば全て取引できる)
– PancakeSwap
– dYdX (仮想通貨の先物取引)

ECR20は、イーサリアムのブロックチェーン上で動作するトークンの統一ルール、規格の一つ

### 日本の仮想通貨法
仮想通貨法には第一号仮想通貨、第二号仮想通貨の分類があり、前者は物品の購入などに際して不特定の者に使用できかつ不特定の者を相手として交換でき、後者は不特定の者を相手として第一号仮想通貨と交換できる
ECR20トークンはイーサリアムと交換可能なことから、第二号仮想通貨に該当する

### uniswap
metamaskなどのwalletがあれば簡単にswapができる
Uniswapでswapを実行すると、metamask上のwalletでも、swapした通貨を保有していることが確認できる。
https://app.uniswap.org/

イーサリアム: UniSwap
Binance Smart Chain: PancakeSwap
Solana : Raydium, Jupiter

Raydimでswapする場合は、PhantomなどSolのwalletを作成して、あとは接続するだけです。

RaydimもUIはUniSwapとかなり似ています。

なるほど、DEXはかなり凄いですね…

【Rust】ステートマシンによるメール判定【Algorithm】

fn isalphanum(c: char) -> bool {
    return c.is_alphabetic() || c.is_numeric();
}

fn isdot(c: char) -> bool {
    return c == '.';
}

fn isatmark(c: char) -> bool {
    return c == '@';
}

fn isend(c: char) -> bool {
    return c == '$';
}

fn isemail(mut s: String) -> bool {
    s += "$";

    let INIT = 0;
    let LOCAL_NOTDOT = 1;
    let LOCAL_DOT = 2;
    let ATMART = 3;
    let DOMAIN_NOTDOT = 4;
    let DOMAIN_DOT = 5;
    let OK = 6;
    let NG = 7;

    let mut state = INIT;

    for c in s.chars() {
        if state == INIT {
            if isalphanum(c) {
                state = LOCAL_NOTDOT;
            } else {
                state = NG;
                break;
            }
        } else if state == LOCAL_NOTDOT {
            if isalphanum(c) {
                state = LOCAL_NOTDOT;
            } else if isdot(c) {
                state = LOCAL_DOT;
            } else if isatmark(c) {
                state = ATMART;
            } else {
                state = NG;
                break;
            }
        } else if state == LOCAL_DOT {
            if isalphanum(c) {
                state = LOCAL_NOTDOT;
            } else {
                state = NG;
                break;
            }
        } else if state == ATMART {
            if isalphanum(c) {
                state = DOMAIN_NOTDOT;
            } else {
                state = NG;
                break;
            }
        } else if state == DOMAIN_NOTDOT {
            if isalphanum(c) {
                state = DOMAIN_NOTDOT;
            } else if isdot(c) {
                state = DOMAIN_DOT;
            } else if isend(c) {
                state = OK;
            } else {
                state = NG;
                break;
            }
        } else if state == DOMAIN_DOT {
            if isalphanum(c) {
                state = DOMAIN_NOTDOT;
            } else {
                state = NG;
                break;
            }
        } else if state == OK {
            break;
        } else if state == NG {
            break;
        } else {
            state = NG
        }
    }
    return state == OK;
}

fn main() {
    let email = "t@g.m".to_string();
    if isemail(email.clone()) {
        println!("メールアドレスです。{}", email);
    } else {
        println!("メールアドレスではありません。");
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.25s
Running `target/debug/basic`
メールアドレスです。t@g.m

【Rust】ボイアーとムーアで過半数の取得を計算する【Algorithm】

### 単純に計算していく方法

fn main() {
    
    let mut vote = [
        "山田太郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), 
        "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), 
        "鈴木花子".to_string(), "鈴木花子".to_string(), "山田太郎".to_string(), "鈴木花子".to_string(), 
    ];

    let n = vote.len();

    let half = n / 2;
    let mut winner = "".to_string();

    for i in 0..n {
        if vote[i] == "" {
            continue;
        }

        let mut count = 1;

        for j in i+1..n {
            if vote[j] == vote[i] {
                count += 1;
                vote[j] = "".to_string();
                if count > half {
                    winner = vote[i].clone();
                    break;
                }
            }
        }
        if winner != "".to_string() {
            break;
        }
    }

    if winner != "".to_string() {
        println!("{}が過半数を取りました", winner);
    } else {
        println!("過半数を取った人はいません");
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/basic`
鈴木花子が過半数を取りました

### ボイアーとムーアのアルゴリズム
最初に最も多い組み合わせを調べ、それが過半数に達しているか計算する。
計算量が少なくて済む。

fn main() {
    
    let vote = [
        "山田太郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), 
        "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), 
        "鈴木花子".to_string(), "鈴木花子".to_string(), "山田太郎".to_string(), "鈴木花子".to_string(), 
    ];

    let n = vote.len();
    let mut candidate = "".to_string();

    let mut survivor = 0;

    for i in 0..n {
        if survivor == 0 {
            candidate = vote[i].clone();
            survivor = 1;
        } else if candidate == vote[i] {
            survivor += 1;
        } else {
            survivor -= 1;
        }
    }
    let mut count = 0;
    if survivor != 0 {
        for i in 0..n {
            if candidate == vote[i] {
                count += 1;
            }
        }
    }

    if count > (n / 2) {
        println!("{}が過半数を取りました。", candidate);
    } else {
        println!("過半数を取った人はいません。");
    }
}

### ソートによるカウント
ソートして真ん中の値のカウントをする。これも美しい。

fn main() {
    
    let mut vote = vec![
        "山田太郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), 
        "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), 
        "鈴木花子".to_string(), "鈴木花子".to_string(), "山田太郎".to_string(), "鈴木花子".to_string(), 
    ];

    let half = vote.len() / 2;

    vote.sort();

    if vote.clone().into_iter().filter(|n| *n == vote[half]).count() > half {
        println!("{}が過半数を取りました。", vote[half]);
    } else {
        println!("過半数を取った人はいません。");
    }
}

### hashmapで計算する

use std::collections::HashMap;

fn main() {
    
    let vote = [
        "山田太郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), 
        "鈴木花子".to_string(), "佐藤三郎".to_string(), "鈴木花子".to_string(), "佐藤三郎".to_string(), 
        "鈴木花子".to_string(), "鈴木花子".to_string(), "山田太郎".to_string(), "鈴木花子".to_string(), 
    ];

    let mut blackboard: HashMap<String, i32> = HashMap::new();

    for v in vote.clone() {
        if !blackboard.contains_key(&v) {
            blackboard.insert(v.clone(),1);
        } else {
            blackboard.insert(v.clone(), 1 + blackboard[&v]);
        }
        println!("{:?}", blackboard);
    }

    let key_with_max_value =blackboard.iter().max_by_key(|entry | entry.1).unwrap();
    if *key_with_max_value.1 as usize > (vote.len() / 2) {
        println!("過半数を取得しました。{:?}", key_with_max_value);
    } else {
        println!("過半数を取った人はいません。");
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/basic`
{“山田太郎”: 1}
{“山田太郎”: 1, “鈴木花子”: 1}
{“山田太郎”: 1, “鈴木花子”: 1, “佐藤三郎”: 1}
{“佐藤三郎”: 1, “山田太郎”: 1, “鈴木花子”: 2}
{“佐藤三郎”: 1, “山田太郎”: 1, “鈴木花子”: 3}
{“佐藤三郎”: 2, “山田太郎”: 1, “鈴木花子”: 3}
{“佐藤三郎”: 2, “山田太郎”: 1, “鈴木花子”: 4}
{“佐藤三郎”: 3, “山田太郎”: 1, “鈴木花子”: 4}
{“佐藤三郎”: 3, “山田太郎”: 1, “鈴木花子”: 5}
{“佐藤三郎”: 3, “山田太郎”: 1, “鈴木花子”: 6}
{“佐藤三郎”: 3, “山田太郎”: 2, “鈴木花子”: 6}
{“佐藤三郎”: 3, “山田太郎”: 2, “鈴木花子”: 7}
過半数を取得しました。(“鈴木花子”, 7)

これが感覚的には一番わかりやすい。でもアルゴリズム的にはボイヤーの方が優れているんだよなー

【Internet Computer】Rust ではじめてのキャニスター開発をやってみる

こちらの記事を参考にRustでDapps開発を行います。
https://smacon.dev/posts/hello-icp-rust/

### dfxのインストール
sh -ci “$(curl -fsSL https://sdk.dfinity.org/install.sh)”

dfxのインストールだが、mac m1チップのARMアーキテクチャではできない。
https://forum.dfinity.org/t/ubuntu-vm-on-macbook-m1-dfx-install-unknown-cpu-type-aarch64/21486

$ uname -m
aarch64

おいおいおい、いきなりつまづいてしまった。これのトラブルシューティングに丸一日かかりました。
どうしてもやりたかったので、結局、別の環境で作ることに。
# uname -m
x86_64

# dfxのプロジェクトファイル実行
# dfx new –type=rust rust_hello
# cd rust_hello
# dfx start –background
# rustup target add wasm32-unknown-unknown
# dfx deploy
Installed code for canister rust_hello_backend, with canister ID bkyz2-fmaaa-aaaaa-qaaaq-cai

# dfx canister call bkyz2-fmaaa-aaaaa-qaaaq-cai greet ‘(“everyone”: text)’
(“Hello, everyone!”)

# dfx canister status bkyz2-fmaaa-aaaaa-qaaaq-cai
Canister status call result for bkyz2-fmaaa-aaaaa-qaaaq-cai.
Status: Running
Controllers: bnz7o-iuaaa-aaaaa-qaaaa-cai wbqzn-2jy52-icft2-erprw-b52eq-e4ixe-4yxqb-5neaf-miz3k-arx3m-gae
Memory allocation: 0 Bytes
Compute allocation: 0 %
Freezing threshold: 2_592_000 Seconds
Idle cycles burned per day: 1_257_808 Cycles
Memory Size: 1_600_075 Bytes
Balance: 3_061_352_105_201 Cycles
Reserved: 0 Cycles
Reserved cycles limit: 5_000_000_000_000 Cycles
Wasm memory limit: 3_221_225_472 Bytes
Wasm memory threshold: 0 Bytes
Module hash: 0xb60872271f3ca7c4ee2b18a54434c344e3095982685ce10f2c7217c08dfb1cf7
Number of queries: 0
Instructions spent in queries: 0
Total query request payload size: 0 Bytes
Total query response payload size: 0 Bytes
Log visibility: controllers

$ dfx stop

どうやら、「デプロイして、キャニスターを実行」という流れになるようだ。
私の理解では、このキャニスターがスマートコントラクト。
そして、FrontはデフォルトでReactが用意されている。

なるほど、

【Rust】rustでメールを送信する

use lettre:: {
    transport::smtp::authentication::Credentials, Message,
    message::{header, SinglePart},
    SmtpTransport, Transport, message::Mailbox, Address
};

#[tokio::main]
async fn main()  {
    
    let username = "hogehoge@gmail.com";
    let app_password = "**** **** **** ****";
    let smtp = "smtp.gmail.com";
    let email = Message::builder()
        .to(Mailbox::new(None, username.parse::<Address>().unwrap()))
        .from(Mailbox::new(None, username.parse::<Address>().unwrap()))
        .subject("題名: Rust Mail Test")
        .singlepart(
            SinglePart::builder()
                .header(header::ContentType::TEXT_PLAIN)
                .body(String::from("本文 Test"))
        )
        .unwrap();
    let credentials = Credentials::new(username.into(), app_password.into());
    let mailer = SmtpTransport::starttls_relay(smtp)
        .unwrap()
        .credentials(credentials)
        .build();
    let result = mailer.send(&email);
    println!("{:?}", result);
}

【Rust】カレンダーを作る【Algorithm】

fn leap_year(year: u32) -> bool {
    if year % 4 == 0 && year % 100 != 0 || year % 400 == 0 {
        return true;
    } 
    false
}

fn day_of_week(year: usize, month: usize) -> usize {
    let mut days_of_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

    if leap_year(year.try_into().unwrap()) {
        days_of_month[2] = 29;
    }

    let day = 1;
    let mut days = 0;

    for y in 1..year {
        if leap_year(y.try_into().unwrap()) {
            days += 366
        } else {
            days += 365
        }
    }

    for m in 1..month {
        days += days_of_month[m as usize]
    }

    days += day;
    return days % 7
}

fn main() {
    let days_of_week_name = ["日".to_string(), "月".to_string(),"火".to_string(),"水".to_string(),"木".to_string(),"金".to_string(),"土".to_string()];

    let mut days_of_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    let year = 2040;
    let month = 3;

    if leap_year(year.try_into().unwrap()) {
        days_of_month[2] = 29;
    }
    let first_day = day_of_week(year, month);

    println!("{}年 {}月", year, month);
    println!("日 月 火 水 木 金 土");
    let emp = " ".to_string();

    print!("{}", emp.repeat(first_day*3));
    for day in 1..(1 + days_of_month[month]) {
        print!("{:<2} ", day);
        if(day + first_day - 1) % 7 == 6 {
            print!("\n");
        }
    }
    print!("\n");
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.14s
Running `target/debug/basic`
2040年 3月
日 月 火 水 木 金 土
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

これは中々面白い!!! そんなに難しくないけど、中々思いつかない!

【Rust】axumのlayer middlewareにstateを渡す方法

from_fn_with_state という書き方がある。
https://docs.rs/axum/latest/axum/middleware/fn.from_fn_with_state.html

    let app_state = model::connection_pool().await.unwrap();
    let app = Router::new()
        .route("/", get(handler))
        .layer(middleware::from_fn_with_state(app_state.clone(),auth_middleware))
        .layer(CsrfLayer::new(config))
        .with_state(app_state);
//

pub async fn auth_middleware(State(state): State<model::AppState>, req: Request,
    next: Next) -> impl IntoResponse {

    let res = next.run(req).await;
    res   
}

なるほどね、先回りしてよく作られてるw

【Rust】with_state(app_state)とCsrfConfigを共存させたい時

### ダメな例
with_stateを複数繋げるのは上手くいかない

 
    let config = CsrfConfig::default();
    let app = axum::Router::new()
        .merge(public_router)
        .merge(private_router)
        .with_state(config.clone())
        .with_state(app_state);
pub async fn handle_hoge(token: CsrfToken, State(state): State<model::AppState>)-> Response {

### 上手くいく例
csrfの方をstateではなく、layerで渡してあげる。

axum_csrf = {version = “0.11.0”,features = [“layer”]}

use axum_csrf::{CsrfToken, CsrfConfig, CsrfLayer};

    let config = CsrfConfig::default();

    let app = Router::new()
        .merge(public_router)
        .merge(private_router)
        .layer(CsrfLayer::new(config))
        .with_state(app_state);
pub async fn handle_hoge(token: CsrfToken, State(state): State<model::AppState>)-> Response {

なるほどね~