🎉

NodeJSで遠隔操作してみた

2024/11/03に公開

暇だったので、nodeで簡単な遠隔操作をしてみることにしました
環境:
OS: MacOS
editor: vscode


デレクトリ:

.
├── test.mp3 <= 3
├── index.html <= 2
└── server.js <= 1

1 directory, 5 files

ソースコード:

index.htmlserver.jsを作成してください

1のコード
main.js
const http = require("http");
const fs = require("fs");
const PORT = 8000;
const html = fs.readFileSync("./index.html", "utf8");
const player = require("play-sound")((opts = {}));
const filePath = "test.mp3";
const { styleText } = require('node:util')

// ユーザー名とパスワードを設定
const USERNAME = "admin";
const PASSWORD = "password";

// isSelectedの状態を保持する
let isRunning = false; // 処理が起動しているかどうかを示すフラグ
let intervalId = null; // 処理を継続するためのID

// HTTP Basic Authenticationをチェック
const authenticate = (req) => {
  const auth = req.headers["authorization"];
  if (!auth) {
    return false; // 認証がない
  }
  const [username, password] = Buffer.from(auth.split(" ")[1], "base64")
    .toString()
    .split(":");
  return username === USERNAME && password === PASSWORD;
};

// サーバーを作成
const server = http.createServer((req, res) => {
  if (!authenticate(req)) {
    res.writeHead(401, { "WWW-Authenticate": "Basic" });
    res.end("Authorization required");
    return;
  }

  // 認証成功時の処理
  if (req.method === "GET") {
    const errorMessage = styleText('green', 'GET :');
    console.log(errorMessage);
    res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
    res.end(html);
  } else if (req.method === "POST" && req.url === "/toggle") {
    const errorMessage = styleText('green', 'POST :');
    console.log(errorMessage);
    let body = "";
    req.on("data", (chunk) => {
      body += chunk;
    });
    req.on("end", () => {
      try {
        const { isSelected } = JSON.parse(body);
        handleToggle(isSelected, res);
      } catch (error) {
        console.error("JSON parsing error:", error);
        res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
        res.end(JSON.stringify({ error: "Invalid JSON" }));
      }
    });
  } else {
    res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
    res.end("Not Found");
  }
});

// 処理の開始または停止
const handleToggle = (isSelected, res) => {
  if (isSelected && !isRunning) {
    isRunning = true;
    const errorMessage = styleText('blue', '開始します');
    console.log(errorMessage);
    intervalId = setInterval(() => {
      const errorMessage = styleText('red', 'StartMp3');
      console.log(errorMessage);
      player.play(filePath, (err) => {
        if (err) {
          console.log(`エラーが発生しました: ${err}`);
        }
      });
    }, 1000); // 1秒ごとにログを出力
  } else if (!isSelected && isRunning) {
    isRunning = false;
    const errorMessage = styleText('blue', '停止します');
    console.log(errorMessage);
    clearInterval(intervalId);
    intervalId = null;
  }

  res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
  res.end(JSON.stringify({ message: "State received" }));
};

// サーバーを起動
server.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

2のコード
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>遠隔操作ボタン</title>
    <style>
        body {
            background-color: #1a1a1a;
            color: #fff;
            font-family: 'Arial', sans-serif;
            overflow: hidden;
        }

        .center {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            position: relative;
        }

        .toggle-btn {
            background-color: #007bff;
            color: #fff;
            padding: 15px 30px;
            border: none;
            border-radius: 10px;
            cursor: pointer;
            font-size: 1.5em;
            position: relative;
            outline: none;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
            transition: transform 0.3s;
            z-index: 1;
        }

        .selected {
            background-color: #26ff00;
            animation: glow 1s infinite alternate;
        }

        @keyframes glow {
            from {
                box-shadow: 0 0 20px rgba(47, 255, 0, 0.5);
            }
            to {
                box-shadow: 0 0 40px rgb(94, 255, 0);
            }
        }

        .error-message {
            color: rgb(115, 255, 0);
            font-weight: bold;
            position: absolute;
            top: 0; /* ボタンの上に表示 */
            left: 50%;
            transform: translateX(-50%);
            display: none; /* 初期は非表示 */
            z-index: 2;
        }
        .server-message {
            color: blue;
            font-weight: bold;
            position: absolute;
            top: 0; /* ボタンの上に表示 */
            left: 50%;
            transform: translateX(-50%);
            display: none; /* 初期は非表示 */
            z-index: 2;
        }

        .server_err {
            background-color: rgb(251, 59, 59);
            color: #fff;
            padding: 15px 30px;
            border: none;
            border-radius: 10px;
            /*cursor: pointer;*/
            font-size: 1.5em;
            position: relative;
            outline: none;
            /*box-shadow: 0 0 10px rgba(0, 0, 0, 0.8);*/
            transition: transform 0.3s;
            z-index: 1;
        }
        .server_sf {
            background-color: rgb(78, 99, 255);
            color: #fff;
            padding: 15px 30px;
            border: none;
            border-radius: 10px;
            /*cursor: pointer;*/
            font-size: 1.5em;
            position: relative;
            outline: none;
            /*box-shadow: 0 0 10px rgba(0, 0, 0, 0.8);*/
            transition: transform 0.3s;
            z-index: 1;
        }
    </style>
</head>
<body>
    <div class="center">
        <div class="server_sf" id="serverMessage">安全</div>
        <div class="server_err" id="errorMessage">エラー</div>
        <button class="toggle-btn" id="toggleButton">実行</button>
    </div>

    <script>
        const button = document.getElementById('toggleButton');
        const errorMessage = document.getElementById('errorMessage');
        const serverMassage = document.getElementById('serverMessage');
        let isSelected = false;

        errorMessage.style.display = 'none'; // 正常な応答時にエラーメッセージを非表示
        serverMassage.style.display = 'block'; // 正常な応答時にエラーメッセージを非表示

        button.addEventListener('click', () => {
            isSelected = !isSelected;
            button.classList.toggle('selected', isSelected);
            button.textContent = isSelected ? "運転中..." : "実行";

            fetch('/toggle', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ isSelected })
            })
            .then(response => {
                console.log('サーバーの応答:', response); // サーバー応答のデバッグ
                if (!response.ok) {
                    throw new Error("サーバーの応答が不正です");
                }
                return response.json();
            })
            .then(data => {
                console.log('サーバーからの応答:', data);
                errorMessage.style.display = 'none'; // 正常な応答時にエラーメッセージを非表示
                serverMassage.style.display = 'block'; // 正常な応答時にエラーメッセージを非表示
            })
            .catch(error => {
                console.error('エラー:', error);
                console.error('サーバーからの応答')
                errorMessage.style.display = 'block'; // エラー時にエラーメッセージを表示
                serverMassage.style.display = 'none'; // 正常な応答時にエラーメッセージを非表示
            });
        });
    </script>
</body>
</html>
3のmp3

mp3のフリー音源を探してきて、作業デレクトリにもっていってtest.mp3と名前を変えればOK

実行
npm install play-sound
node main.js

そしたらhttp://localhost:8000に移動して
あと一応パスワードをつけといたんで、admin passwordログインできますが

server.jsの一部分
// ユーザー名とパスワードを設定
const USERNAME = "admin";
const PASSWORD = "password";

ここの部分で設定できます

最後に

重要

他のパソコンや端末でやてみたいと思うかもしれませんが、http://localhost:8000このURLを打ち込んでもうまくいきません!
ターミナルで

ifconfig

でわかります

僕の場合
% ifconfig   
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
	options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>
	inet 127.0.0.1 netmask 0xff000000 <= この行!
	inet6 ::1 prefixlen 128 
	inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
	nd6 options=201<PERFORMNUD,DAD> ...

つまり他のパソコンや端末で動かすためには、
調べたやつ:127.0.0.1 :8000ということなのでhttp://127.0.0.1:8000に行けばうまくいきます

よくわからない時はGeminiChatGPTに聞くとわかったりします

Discussion