はじめに
アドカレ何か書くことあるかなー、今年は何事もなく終えるかなー、と考えていたところにXでのある投稿が目に入りました
それは
Symbolで静的ファイルのWebページをホスティングしたりできるのかな
というものでしたが、これはいいネタだと思いアドカレ執筆を始めます。
SymbolなんですがMetalというプロトコルを使います。Metalに関しては以下を読んでみてください。面白いです。
手数料さえ払えばどんな大きいサイズのファイルでも理論上は可能です。
静的ページはなんでもいいんですが、ちょっとそれっぽくVite + Reactでシンプルなページ遷移ができるモノにします。なお僕はReactの知識はあんまりありません。ここは本題じゃないのでなんでもいいです。
ポイントは1枚のファイルにすることです。(デコードを自分でするなら別に1枚じゃなくてもいいけど)今回はViteで生成したWebサイトのbundleファイルをhtmlに全部直接書き込むという方法を取ります。
静的ページを作成
ここは本題じゃないのでささっと
npm create vite@latest static-page --template react
// 今回は tsx にした
cd static-page
static-page install
作成したら
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
export default defineConfig({
plugins: [react()],
build: {
rollupOptions: {
output: {
entryFileNames: 'bundle.js',
chunkFileNames: 'bundle.js',
assetFileNames: 'assets/[name].[ext]',
},
},
},
})
import React, { useState } from 'react';
type Page = 'page1' | 'page2';
const App: React.FC = () => {
const [currentPage, setCurrentPage] = useState<Page>('page1');
const handleTabClick = (page: Page) => {
setCurrentPage(page);
};
return (
<div>
<div style={{ display: 'flex', marginBottom: '20px' }}>
<button onClick={() => handleTabClick('page1')}>Page 1</button>
<button onClick={() => handleTabClick('page2')}>Page 2</button>
</div>
{currentPage === 'page1' && (
<div>
<h1>Page 1</h1>
<p>This is Page 1 content.</p>
</div>
)}
{currentPage === 'page2' && (
<div>
<h1>Page 2</h1>
<p>This is Page 2 content.</p>
</div>
)}
</div>
);
}
export default App;
最下部にスタイル追加
.app-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
text-align: center;
}
#root {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
}
.tabs {
display: flex;
margin-bottom: 20px;
}
.tabs button {
margin: 0 10px;
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
.content {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
}
これだけです。
ビルドします
npm run build
indexファイルのjsとcss読み込み箇所に直接書き込む
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<style>
/* ここにassets/index.cssをコピペする */
</style>
</head>
<body>
<div id="root"></div>
<script>
// ここにbundle.jsをコピペする
</script>
</body>
</html>
これだけです。もし画像などを使用したい場合には
- base64にしてファイルに直接書き込む
- 画像をmetalでオンチェーンにする
という選択肢があるかなーと思いますが今回は使ってません
Metalを使ってチェーンに刻む
metal cliのインストール
npm install -g metal-on-symbol
NODE情報をセットする
僕はMACなので
export NODE_URL=https://example.jp:3001
ちなみにNODEは
メインネットなら
https://symbolnodes.org/nodes/
テストネットなら
https://symbolnodes.org/nodes_testnet/
ここからAPIノードを選ぶ
さっそくForgeする
viteで作成したアプリケーションのrootで作業する場合
metal forge ./dist/index.html --seal 2
--seal 2
というオプションを必ずつけます
これによりmimeTypeとファイルサイズが付加情報としてオンチェーンになります
秘密鍵を聞かれるので貼り付けてEnter
アナウンスするか聞かれるのでYESを選択し承認を待ちます。
承認されるとこんな感じで結果が表示されます。
METAL IDを控えてノードに以下のようなエンドポイントでアクセスすると
そのまま作成したWEBサイトが表示されます
注意点
Forgeするときはどのノードにアナウンスしてもいいんですが、REST経由で表示できるのは特定のノードのみです。
ノード側にちょっとした設定が必要で以下のノードの運営者さんが協力してくれました。
※なお、このリストは2024/12/17日時点で表示が確認できていますが、ノードのアップデートなどがあれば表示されない可能性があります。
-
MainNet
https://pasomi.net:3001/metadata/metal/Fe6p1K4dmVekyBqbqrNqUhNc4X37sFsA4NMGAPd1cbjtdN
https://vmi1560137.contaboserver.net:3001/metadata/metal/Fe6p1K4dmVekyBqbqrNqUhNc4X37sFsA4NMGAPd1cbjtdN
https://symbol-mikun.net:3001/metadata/metal/Fe6p1K4dmVekyBqbqrNqUhNc4X37sFsA4NMGAPd1cbjtdN
https://finnel.harvestasya.com:3001/metadata/metal/Fe6p1K4dmVekyBqbqrNqUhNc4X37sFsA4NMGAPd1cbjtdN
https://symbol.harvesting-sweet-potatoes.club:3001/metadata/metal/Fe6p1K4dmVekyBqbqrNqUhNc4X37sFsA4NMGAPd1cbjtdN
https://00.symbol-node.com:3001/metadata/metal/Fe6p1K4dmVekyBqbqrNqUhNc4X37sFsA4NMGAPd1cbjtdN
https://01.symbol-node.com:3001/metadata/metal/Fe6p1K4dmVekyBqbqrNqUhNc4X37sFsA4NMGAPd1cbjtdN
https://02.symbol-node.com:3001/metadata/metal/Fe6p1K4dmVekyBqbqrNqUhNc4X37sFsA4NMGAPd1cbjtdN
https://03.symbol-node.com:3001/metadata/metal/Fe6p1K4dmVekyBqbqrNqUhNc4X37sFsA4NMGAPd1cbjtdN
https://villhell-symbol-mainnet.net:3001/metadata/metal/Fe6p1K4dmVekyBqbqrNqUhNc4X37sFsA4NMGAPd1cbjtdN
https://node.sixis.xyz:3001/metadata/metal/Fe6p1K4dmVekyBqbqrNqUhNc4X37sFsA4NMGAPd1cbjtdN -
TestNet
https://2.dusanjp.com:3001/metadata/metal/Fe3kBsPpMgDstJq3uALpYtUXm6pfuyx1y8tNs1MYohYX2d
https://sakia.harvestasya.com:3001/metadata/metal/FeERXDZeHMxjXYU5FmjjJdmK2oMRXtDg5yNP7Ms4KE924y
REST経由で直接表示できなくともデコードはSDKを使用すれば可能です
https://github.com/OPENSPHERE-Inc/metal-on-symbol?tab=readme-ov-file#6-sdk-typescript--ecmascript
さいごに
ざくっと書いたので言葉足らずな点もあるかもしれませんが、ひとまず以上で記事を終えます。
ちなみにbundle.jsやcssファイル単体をmetalでforgeしてそれをhtmlファイルで読み込むという方法でも良いと思います
分散型ホスティング面白いですね
追記。ちなみに静的じゃなくて動的でもMetalを使ってわりと簡単に実現できると思います