Compare commits
10 Commits
feature/ar
...
main
Author | SHA1 | Date | |
---|---|---|---|
46b08a46b5 | |||
![]() |
184eba34e1 | ||
![]() |
b76affe2f7 | ||
![]() |
5c9c5ba9e0 | ||
![]() |
2a65449959 | ||
![]() |
c91e2006b4 | ||
![]() |
fa31073482 | ||
![]() |
21bd8e37da | ||
![]() |
a023dacbc0 | ||
![]() |
2454db7d13 |
246
docs/ar-xr.md
Normal file
246
docs/ar-xr.md
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
# [開発]AR/XRの利用ガイドライン
|
||||||
|
|
||||||
|
## AR/XEについて定義
|
||||||
|
|
||||||
|
空間の概念
|
||||||
|
|
||||||
|
* 空間の中に仮想オブジェクトが配置
|
||||||
|
* ユーザーが空間内を自由に移動または視点操作できる
|
||||||
|
|
||||||
|
## 利用例と対応技術
|
||||||
|
|
||||||
|
1. **GPS、カメラなどのセンサ情報と連動**
|
||||||
|
- 仮想オブジェクトを現実に重ねる
|
||||||
|
- 例:ポケモンGOのように位置情報と連動して表示される仮想キャラクター。
|
||||||
|
2. **ユーザーとのインタラクション**
|
||||||
|
- タップ、ジェスチャー、視線、音声などを通じた操作が可能。
|
||||||
|
- 例:展覧会アプリでアート作品に近づくと詳細情報が表示される。
|
||||||
|
3. **空間認識 / 環境マッピング**
|
||||||
|
- ARKitやARCoreなどで現実の床・壁・物体を認識して、仮想オブジェクトを自然に配置。
|
||||||
|
- 例:家具ARアプリが部屋の床にテーブルを正確に置く
|
||||||
|
4. **空間を活かしたUX設計**
|
||||||
|
- 単なるUI配置ではなく、「空間に存在する意味」を考慮した設計。
|
||||||
|
- 例:不動産カタログがモデルルーム内を歩き回るUXで部屋の広さや窓の位置を体験できる。
|
||||||
|
|
||||||
|
| 利用例 | 空間性 | 使用技術 | 特記事項 |
|
||||||
|
| ---------------- | -------- | ----------------- | -------------------------------- |
|
||||||
|
| 歩行型Web展覧会 | あり | WebXR + A-Frame等 | 空間移動・近接操作あり |
|
||||||
|
| ポケモンGO | あり | ARKit/ARCore | GPS連動・カメラ合成あり |
|
||||||
|
| 商品カタログ(3D) | 条件付き | WebAR / Unity等 | 操作・空間性の有無で評価分かれる |
|
||||||
|
|
||||||
|
### 類似技術(ARではない例)
|
||||||
|
|
||||||
|
| 例 | 理由 |
|
||||||
|
| -------------------------------------------------- | -------------------------------------------------------- |
|
||||||
|
| カメラ映像の上にキャラクターを表示するだけのアプリ | 空間と連動していないため「ただの合成」 |
|
||||||
|
| 背景がカメラ映像の3Dゲーム | 現実との関係がないのでARではない |
|
||||||
|
| Instagramのエフェクトの一部(目に星を重ねるなど) | 空間ARというよりはフェイストラッキング or エフェクト処理 |
|
||||||
|
|
||||||
|
## スタック
|
||||||
|
|
||||||
|
### フロントエンド(AR体験を提供する側)
|
||||||
|
|
||||||
|
| デバイス | 技術・ライブラリ |
|
||||||
|
| -------- | -------------------- |
|
||||||
|
| iOS | ARKit(Apple純正) |
|
||||||
|
| Android | ARCore(Google純正) |
|
||||||
|
|
||||||
|
**(Web向けAR)**
|
||||||
|
|
||||||
|
| ライブラリ | 特徴 | 難易度 | 拡張性 | 主な用途 |
|
||||||
|
| ---------- | --------------------------------------------- | ------ | -------- | -------------------- |
|
||||||
|
| A-Frame | HTMLベースで簡単に3Dコンテンツ。初心者向き | 低 | 中 | プロトタイピングなど |
|
||||||
|
| Three.js | 高機能3Dライブラリ。低レベル制御が可能 | 中〜高 | 高 | 複雑な3D表現 |
|
||||||
|
| AR.js | WebAR用ライブラリ。Three.js/A-Frameと併用可能 | 中 | 中 | マーカーAR、GPS AR |
|
||||||
|
| 8thWall | 高精度WebARプラットフォーム(有料) | 低 | 高 | 実用ARアプリ |
|
||||||
|
| WebXR API | ブラウザ標準のAR/VR API | 高 | 非常に高 | 本格的なAR/VRアプリ |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* iOSの制約ポイント(2025年)
|
||||||
|
* カメラアクセス Safariのみが原則的に対応(PWAやChromeは制限あり)
|
||||||
|
* WebXR API:サポートされていない(2025年現在)
|
||||||
|
* WebRTC/MediaDevices getUserMediaはOK。(AR.jsの一部機能が動作不安定)
|
||||||
|
|
||||||
|
その他の仕様する技術について
|
||||||
|
|
||||||
|
| Flutter | |
|
||||||
|
| ------- | --------------------------------------------- |
|
||||||
|
| Unity | AR Foundation(ARKit + ARCoreの抽象化) |
|
||||||
|
| Flutter | ar_flutter_plugin(公式ARSDKのFlutterラッパー) |
|
||||||
|
| | |
|
||||||
|
|
||||||
|
|
||||||
|
### バックエンド
|
||||||
|
|
||||||
|
必要な場合のみ実装
|
||||||
|
|
||||||
|
*クラウド処理
|
||||||
|
* 画像認識AI
|
||||||
|
* 地図情報
|
||||||
|
* マルチユーザー同期
|
||||||
|
|
||||||
|
WebSocketやMQTTでリアルタイム連携する
|
||||||
|
|
||||||
|
|
||||||
|
### A-Frame(エーフレーム)
|
||||||
|
|
||||||
|
Webブラウザ上で3DコンテンツやVR/AR体験を作れるHTMLベースのフレームワーク
|
||||||
|
JavaScriptが得意でなくても、**HTMLタグ感覚で簡単に使える**
|
||||||
|
|
||||||
|
* 公式チュートリアル: https://aframe.io/docs/master/introduction/
|
||||||
|
|
||||||
|
#### 使用方法について
|
||||||
|
|
||||||
|
A-FrameはCDNで使えます。
|
||||||
|
HTMLの<head>に以下を追加する
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Three.js + AR.js
|
||||||
|
|
||||||
|
Three.jsは WebGLを抽象化し、高機能な3D描画をJavaScriptで行える軽量ライブラリです。
|
||||||
|
ARやVRだけでなくゲームやインタラクティブなグラフィック表現にも使われます
|
||||||
|
|
||||||
|
|
||||||
|
- 公式: https://threejs.org/
|
||||||
|
- GitHub: https://github.com/mrdoob/three.js/
|
||||||
|
|
||||||
|
|
||||||
|
AR.jsは、Three.jsベースで動作する**WebARライブラリ**。
|
||||||
|
マーカー認識や位置情報を使ったAR体験を提供。
|
||||||
|
|
||||||
|
- 公式: https://ar-js-org.github.io/AR.js-Docs/
|
||||||
|
|
||||||
|
#### 特徴
|
||||||
|
|
||||||
|
**Three.js**
|
||||||
|
- カスタマイズ性が高く、細かい3D制御が可能
|
||||||
|
- WebAR/VR対応にも拡張しやすい
|
||||||
|
- A-Frameに比べて**記述量は多め**だが、**柔軟性が高い**
|
||||||
|
|
||||||
|
**AR.js**
|
||||||
|
- モバイルブラウザでも軽快に動作(60fpsも実現可能)
|
||||||
|
- **マーカーベースAR**(Hiroマーカーなど)と**位置情報ベースAR**の両方に対応
|
||||||
|
- **A-Frameとも連携可能**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 使用方法について(Three.js + AR.js)
|
||||||
|
|
||||||
|
Three.jsのシーンを作成し、AR.jsと連携してカメラ映像と重ねて表示します。
|
||||||
|
主に以下のステップで構成されます:
|
||||||
|
|
||||||
|
1. カメラの取得(`ARjs.Context`)
|
||||||
|
2. Three.jsのシーン、カメラ、レンダラーの準備
|
||||||
|
3. マーカー検出(`ARjs.MarkerControls`)を使って3Dモデル表示
|
||||||
|
|
||||||
|
#### サンプルコード(マーカーベースAR)
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>AR.js マーカーベース サンプル</title>
|
||||||
|
<script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/gh/AR-js-org/AR.js@3.3.2/aframe/build/aframe-ar.min.js"></script>
|
||||||
|
<style>
|
||||||
|
body { margin: 0; overflow: hidden; }
|
||||||
|
#info {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
color: white;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="info">「Hiroマーカー」をカメラにかざしてください</div>
|
||||||
|
|
||||||
|
<a-scene embedded arjs="sourceType: webcam; debugUIEnabled: false;">
|
||||||
|
<!-- マーカーが認識されたときに表示される青い箱 -->
|
||||||
|
<a-marker preset="hiro">
|
||||||
|
<a-box position="0 0.5 0" material="color: blue;" shadow></a-box>
|
||||||
|
<a-text value="Hello AR!" position="-0.5 1 0" color="white"></a-text>
|
||||||
|
</a-marker>
|
||||||
|
|
||||||
|
<!-- カメラ -->
|
||||||
|
<a-entity camera></a-entity>
|
||||||
|
</a-scene>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
* Webカメラ対応のPCまたはスマホのブラウザ
|
||||||
|
*
|
||||||
|
|
||||||
|
## A-Frameの基本
|
||||||
|
|
||||||
|
### A-Frameの導入
|
||||||
|
A-FrameはCDNで使えます。HTMLの`<head>`に以下を追加するだけ:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
| タグ | 説明 |
|
||||||
|
| -------------- | ------------------------------------------ |
|
||||||
|
| `<a-scene>` | A-Frameの世界のルート(必須) |
|
||||||
|
| `<a-box>` | 箱を表示(位置、回転、色、サイズを指定可) |
|
||||||
|
| `<a-sphere>` | 球体 |
|
||||||
|
| `<a-cylinder>` | 円柱 |
|
||||||
|
| `<a-plane>` | 平面。地面に使われることが多い |
|
||||||
|
| `<a-text>` | テキスト表示 |
|
||||||
|
| `<a-camera>` | 視点を定義。省略すると自動的に追加される |
|
||||||
|
| `<a-light>` | 光源。明るさや影の描画に必要 |
|
||||||
|
|
||||||
|
|
||||||
|
## サンプルコード
|
||||||
|
|
||||||
|
### A-Frameで作る歩行型Webページ
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>歩行型Webページ</title>
|
||||||
|
<script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a-scene>
|
||||||
|
<!-- 地面 -->
|
||||||
|
<a-plane position="0 0 0" rotation="-90 0 0" width="50" height="50" color="#7BC8A4"></a-plane>
|
||||||
|
|
||||||
|
<!-- カメラとコントロール(WASDキーで歩行可能) -->
|
||||||
|
<a-entity camera wasd-controls look-controls position="0 1.6 5"></a-entity>
|
||||||
|
|
||||||
|
<!-- 建物風のオブジェクト -->
|
||||||
|
<a-box position="0 0.5 -5" depth="5" height="1" width="5" color="#4CC3D9"></a-box>
|
||||||
|
<a-box position="10 0.5 -5" depth="5" height="1" width="5" color="#FFC65D"></a-box>
|
||||||
|
<a-sphere position="-10 1 -5" radius="1" color="#EF2D5E"></a-sphere>
|
||||||
|
</a-scene>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### 特徴
|
||||||
|
- **マウス視点移動**:マウスで見回せます。
|
||||||
|
- **WASDキーで歩行**:前後左右に移動できます。
|
||||||
|
- **HTMLだけで構成**:JavaScriptなしでA-Frameがほとんど自動処理。
|
||||||
|
|
||||||
|
#### 移動の制御
|
||||||
|
|
||||||
|
- `wasd-controls`: W/A/S/Dキーで歩けるようにする
|
||||||
|
- `look-controls`: マウスで視点を変える
|
||||||
|
|
||||||
|
#### 応用例
|
||||||
|
|
||||||
|
* [ホバーイベント:](../src\front\ar\afreame\aframe-hover.html)
|
4
docs/blockcheen.md
Normal file
4
docs/blockcheen.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# [技術][2025]ブロックチェーンの利用ガイドライン
|
||||||
|
|
||||||
|
サプライチェーンにブロックチェーンを導入することで
|
||||||
|
トレーサビリティの確保、改ざん防止、透明性の向上といった効果が得られます
|
42
docs/front/ar/WebXR.html
Normal file
42
docs/front/ar/WebXR.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<!-- index.html -->
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Simple WebXR AR</title>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/three@0.152.2/examples/jsm/webxr/ARButton.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body style="margin: 0; overflow: hidden;">
|
||||||
|
<script type="module">
|
||||||
|
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.module.js';
|
||||||
|
import { ARButton } from 'https://cdn.jsdelivr.net/npm/three@0.152.2/examples/jsm/webxr/ARButton.js';
|
||||||
|
|
||||||
|
const scene = new THREE.Scene();
|
||||||
|
const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20);
|
||||||
|
|
||||||
|
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
renderer.xr.enabled = true;
|
||||||
|
document.body.appendChild(renderer.domElement);
|
||||||
|
document.body.appendChild(ARButton.createButton(renderer));
|
||||||
|
|
||||||
|
const geometry = new THREE.BoxGeometry(0.1, 0.1, 0.1);
|
||||||
|
const material = new THREE.MeshNormalMaterial();
|
||||||
|
const cube = new THREE.Mesh(geometry, material);
|
||||||
|
cube.position.set(0, 0, -0.5);
|
||||||
|
scene.add(cube);
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
renderer.setAnimationLoop(() => {
|
||||||
|
cube.rotation.y += 0.01;
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
animate();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
74
docs/front/ar/WebXRCamera.html
Normal file
74
docs/front/ar/WebXRCamera.html
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>WebXR AR Sample</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<script type="module">
|
||||||
|
import * as THREE from 'https://cdn.skypack.dev/three@0.152.2';
|
||||||
|
import { ARButton } from 'https://cdn.skypack.dev/three@0.152.2/examples/jsm/webxr/ARButton.js';
|
||||||
|
|
||||||
|
let camera, scene, renderer;
|
||||||
|
let controller;
|
||||||
|
|
||||||
|
init();
|
||||||
|
animate();
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
// Renderer
|
||||||
|
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||||
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
renderer.xr.enabled = true;
|
||||||
|
document.body.appendChild(renderer.domElement);
|
||||||
|
|
||||||
|
// ARボタンを追加(Hit-test機能必要)
|
||||||
|
document.body.appendChild(
|
||||||
|
ARButton.createButton(renderer, { requiredFeatures: ['hit-test'] })
|
||||||
|
);
|
||||||
|
|
||||||
|
// SceneとCamera
|
||||||
|
scene = new THREE.Scene();
|
||||||
|
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20);
|
||||||
|
|
||||||
|
// 光源
|
||||||
|
const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 1);
|
||||||
|
light.position.set(0.5, 1, 0.25);
|
||||||
|
scene.add(light);
|
||||||
|
|
||||||
|
// 立方体を作成
|
||||||
|
const geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
|
||||||
|
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
|
||||||
|
const cube = new THREE.Mesh(geometry, material);
|
||||||
|
cube.position.set(0, 0, -0.5); // カメラの前に表示
|
||||||
|
scene.add(cube);
|
||||||
|
|
||||||
|
// コントローラ
|
||||||
|
controller = renderer.xr.getController(0);
|
||||||
|
scene.add(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
renderer.setAnimationLoop(render);
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
21
docs/front/ar/aframe.html
Normal file
21
docs/front/ar/aframe.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>歩行型Webページ</title>
|
||||||
|
<script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a-scene>
|
||||||
|
<!-- 地面 -->
|
||||||
|
<a-plane position="0 0 0" rotation="-90 0 0" width="50" height="50" color="#7BC8A4"></a-plane>
|
||||||
|
|
||||||
|
<!-- カメラとコントロール(WASDキーで歩行可能) -->
|
||||||
|
<a-entity camera wasd-controls look-controls position="0 1.6 5"></a-entity>
|
||||||
|
|
||||||
|
<!-- 建物風のオブジェクト -->
|
||||||
|
<a-box position="0 0.5 -5" depth="5" height="1" width="5" color="#4CC3D9"></a-box>
|
||||||
|
<a-box position="10 0.5 -5" depth="5" height="1" width="5" color="#FFC65D"></a-box>
|
||||||
|
<a-sphere position="-10 1 -5" radius="1" color="#EF2D5E"></a-sphere>
|
||||||
|
</a-scene>
|
||||||
|
</body>
|
||||||
|
</html>
|
0
docs/front/assets/cloud-storage.js
Normal file
0
docs/front/assets/cloud-storage.js
Normal file
11
docs/front/assets/data/test_data_1.csv
Normal file
11
docs/front/assets/data/test_data_1.csv
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
ID,Name,Age,Email
|
||||||
|
1,User_1,44,user1@example.com
|
||||||
|
2,User_2,20,user2@example.com
|
||||||
|
3,User_3,67,user3@example.com
|
||||||
|
4,User_4,49,user4@example.com
|
||||||
|
5,User_5,56,user5@example.com
|
||||||
|
6,User_6,63,user6@example.com
|
||||||
|
7,User_7,31,user7@example.com
|
||||||
|
8,User_8,67,user8@example.com
|
||||||
|
9,User_9,41,user9@example.com
|
||||||
|
10,User_10,30,user10@example.com
|
|
11
docs/front/assets/data/test_data_2.csv
Normal file
11
docs/front/assets/data/test_data_2.csv
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
ID,Name,Age,Email
|
||||||
|
1,User_1,37,user1@example.com
|
||||||
|
2,User_2,30,user2@example.com
|
||||||
|
3,User_3,63,user3@example.com
|
||||||
|
4,User_4,42,user4@example.com
|
||||||
|
5,User_5,31,user5@example.com
|
||||||
|
6,User_6,63,user6@example.com
|
||||||
|
7,User_7,28,user7@example.com
|
||||||
|
8,User_8,20,user8@example.com
|
||||||
|
9,User_9,42,user9@example.com
|
||||||
|
10,User_10,69,user10@example.com
|
|
79
docs/front/assets/download.js
Normal file
79
docs/front/assets/download.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* @description downloadボタンを押下したらCSVファイルをzip圧縮してダウンロードする
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// ダウンロードボタン
|
||||||
|
const downloadButton = document.getElementById('download-button');
|
||||||
|
const downloadButtonFile = document.getElementById('download-button-file');
|
||||||
|
|
||||||
|
// ダウンロードボタンを押下した時の処理を記載する
|
||||||
|
downloadButton.addEventListener('click', async () => {
|
||||||
|
// ダウンロードボタンを無効化する
|
||||||
|
downloadButton.disabled = true;
|
||||||
|
// ダウンロード処理を実行する
|
||||||
|
try {
|
||||||
|
const zip = new JSZip();
|
||||||
|
// ZIPにファイルを追加
|
||||||
|
zip.file("hello.txt", "Hello, this is a ZIP file!");
|
||||||
|
zip.file("world.txt", "Hello, this is a ZIP file!");
|
||||||
|
|
||||||
|
// ZIPを生成してダウンロード
|
||||||
|
const zipBlob = await zip.generateAsync({ type: "blob" });
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = URL.createObjectURL(zipBlob);
|
||||||
|
a.download = "example.zip";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
} catch (e) {
|
||||||
|
// エラーが発生した場合
|
||||||
|
console.error(e);
|
||||||
|
alert('ダウンロードに失敗しました');
|
||||||
|
} finally {
|
||||||
|
// ダウンロードボタンを有効化する
|
||||||
|
downloadButton.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
downloadButtonFile.addEventListener('click', async () => {
|
||||||
|
// ダウンロードボタンを無効化する
|
||||||
|
downloadButtonFile.disabled = true;
|
||||||
|
try {
|
||||||
|
const files = [
|
||||||
|
{ name: "text1.csv", url: "assets/data/test_data_1.csv" },
|
||||||
|
{ name: "text2.csv", url: "assets/data/test_data_2.csv" }
|
||||||
|
];
|
||||||
|
|
||||||
|
const zip = new JSZip();
|
||||||
|
|
||||||
|
// CSV ファイルを取得して ZIP に追加
|
||||||
|
await Promise.all(
|
||||||
|
files.map(async (file) => {
|
||||||
|
const response = await fetch(file.url);
|
||||||
|
if (!response.ok) throw new Error(`Failed to fetch ${file.name}`);
|
||||||
|
const text = await response.text();
|
||||||
|
zip.file(file.name, text);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// ZIP を生成してダウンロード
|
||||||
|
zip.generateAsync({ type: "blob" }).then((blob) => {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = URL.createObjectURL(blob);
|
||||||
|
link.download = "files.zip";
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
}).catch(console.error)
|
||||||
|
} catch (e) {
|
||||||
|
// エラーが発生した場合
|
||||||
|
console.error(e);
|
||||||
|
alert('ダウンロードに失敗しました');
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// ダウンロードボタンを有効化する
|
||||||
|
downloadButtonFile.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
72
docs/front/assets/downloadFromServer.js
Normal file
72
docs/front/assets/downloadFromServer.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* @description downloadボタンを押下したらCSVファイルをzip圧縮してダウンロードする
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// ダウンロードボタン
|
||||||
|
const downloadButtonServer = document.getElementById('download-button-server');
|
||||||
|
|
||||||
|
// ダウンロードボタンを押下した時の処理を記載する
|
||||||
|
downloadButtonServer.addEventListener('click', async () => {
|
||||||
|
// Blobで取得する方法
|
||||||
|
console.log('downloadButtonServer');
|
||||||
|
// ダウンロードボタンを無効化する
|
||||||
|
downloadButtonServer.disabled = true;
|
||||||
|
// ダウンロード処理を実行する
|
||||||
|
try {
|
||||||
|
// localhost:3000/downdload にリクエストを送る
|
||||||
|
const response = await fetch('http://localhost:3000/downdload');
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch');
|
||||||
|
|
||||||
|
// ZIPファイルを取得
|
||||||
|
const zipBlob = await response.blob();
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = URL.createObjectURL(zipBlob);
|
||||||
|
a.download = "serverFile.zip";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
} catch (e) {
|
||||||
|
// エラーが発生した場合
|
||||||
|
console.error(e);
|
||||||
|
alert('ダウンロードに失敗しました');
|
||||||
|
} finally {
|
||||||
|
// ダウンロードボタンを有効化する
|
||||||
|
downloadButtonServer.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ダウンロードボタン(アップロード)
|
||||||
|
const downloadButtonUpload = document.getElementById('download-button-upload');
|
||||||
|
|
||||||
|
// ダウンロードボタンを押下した時の処理を記載する
|
||||||
|
downloadButtonUpload.addEventListener('click', async () => {
|
||||||
|
console.log('downloadButtonUpload');
|
||||||
|
// ダウンロードボタンを無効化する
|
||||||
|
downloadButtonUpload.disabled = true;
|
||||||
|
// サーバーにアップロード処理APIを送信する
|
||||||
|
try {
|
||||||
|
// localhost:3000/generate-zip にリクエストを送る
|
||||||
|
const response = await fetch('http://localhost:3000/generate-zip');
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch');
|
||||||
|
|
||||||
|
// レスポンスからURLを取得
|
||||||
|
const { url } = await response.json();
|
||||||
|
// 取得したURLを開く
|
||||||
|
window.open(url);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
// エラーが発生した場合
|
||||||
|
console.error(e);
|
||||||
|
alert('ダウンロードに失敗しました');
|
||||||
|
} finally {
|
||||||
|
// ダウンロードボタンを有効化する
|
||||||
|
downloadButtonUpload.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
5
docs/front/assets/index.js
Normal file
5
docs/front/assets/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
// Message
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
document.getElementById("message").textContent = "Hello, JavaScript!";
|
||||||
|
});
|
13
docs/front/assets/lib/jszip.min.js
vendored
Normal file
13
docs/front/assets/lib/jszip.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
21
docs/front/download.html
Normal file
21
docs/front/download.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
||||||
|
<title>Download</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>JavaScript Develop Download To Front Side</h1>
|
||||||
|
<p >Zip File Download</p>
|
||||||
|
<button id="download-button">Zip Download(From Content)</button>
|
||||||
|
<button id="download-button-file">Zip Download(From File)</button>
|
||||||
|
<p >Server </p>
|
||||||
|
<button id="download-button-server">Zip Download(From Server)</button>
|
||||||
|
<button id="download-button-upload">Zip Download(upload)</button>
|
||||||
|
<p >Google Storage Link</p>
|
||||||
|
<script src="assets/download.js"></script>
|
||||||
|
<script src="assets/downloadFromServer.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
15
docs/front/gcs.html
Normal file
15
docs/front/gcs.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
||||||
|
<title>Google Cloud Storage</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>JavaScript Develop Download To Google Cloud Storage</h1>
|
||||||
|
<p >Zip File Download</p>
|
||||||
|
<button id="download-button">Zip Download(GCS)</button>
|
||||||
|
<script src="assets/cloud-storage.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
13
docs/front/index.html
Normal file
13
docs/front/index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>JavaScript Develop Debug</h1>
|
||||||
|
<p id="message">Open the console to see the output</p>
|
||||||
|
<script src="assets/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
199
docs/minio.md
Normal file
199
docs/minio.md
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
# MinIO 利用ガイド
|
||||||
|
|
||||||
|
## 概要
|
||||||
|
|
||||||
|
[MinIO](https://min.io/) は Amazon S3 互換のオブジェクトストレージサーバー。軽量かつシンプルな構成で、オンプレミスやクラウド上に S3 互換のストレージ環境を構築できる。
|
||||||
|
|
||||||
|
高可用構成やセキュリティ機能も備えており、小規模から大規模用途まで幅広く利用できる。本記事では MinIO の導入方法、仕様要件、ユースケース、基本的な利用方法についてまとめる。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. インストール方法
|
||||||
|
|
||||||
|
### 1.1 Docker を使う場合
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -p 9000:9000 -p 9001:9001 \
|
||||||
|
--name minio \
|
||||||
|
-e "MINIO_ROOT_USER=admin" \
|
||||||
|
-e "MINIO_ROOT_PASSWORD=password123" \
|
||||||
|
-v /mnt/data:/data \
|
||||||
|
quay.io/minio/minio server /data --console-address ":9001"
|
||||||
|
```
|
||||||
|
|
||||||
|
- `9000`: S3互換API用ポート
|
||||||
|
- `9001`: Webコンソール用ポート
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.2 Docker Compose を使う場合
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
minio:
|
||||||
|
image: quay.io/minio/minio
|
||||||
|
ports:
|
||||||
|
- "9000:9000"
|
||||||
|
- "9001:9001"
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: admin
|
||||||
|
MINIO_ROOT_PASSWORD: password123
|
||||||
|
command: server /data --console-address ":9001"
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
|
起動コマンド:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.3 バイナリから直接実行する場合(Linux)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://dl.min.io/server/minio/release/linux-amd64/minio
|
||||||
|
chmod +x minio
|
||||||
|
sudo mv minio /usr/local/bin/
|
||||||
|
|
||||||
|
export MINIO_ROOT_USER=admin
|
||||||
|
export MINIO_ROOT_PASSWORD=password123
|
||||||
|
minio server /mnt/data --console-address ":9001"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 仕様要件
|
||||||
|
|
||||||
|
| 項目 | 推奨構成例 |
|
||||||
|
|--------------|------------------------------|
|
||||||
|
| OS | Linux(Ubuntu 22.04 など) |
|
||||||
|
| CPU | 1コア以上(最低限) |
|
||||||
|
| メモリ | 1GB 以上(2GB 以上推奨) |
|
||||||
|
| ストレージ | SSD または HDD |
|
||||||
|
| 通信 | ポート 9000 / 9001(開放) |
|
||||||
|
|
||||||
|
- HTTPS を使用する場合は TLS 証明書(Let's Encrypt など)を導入
|
||||||
|
- リバースプロキシ(Nginx/Caddyなど)経由での公開も可能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. ユースケース・機能
|
||||||
|
|
||||||
|
### 主なユースケース
|
||||||
|
|
||||||
|
- バックアップ保存
|
||||||
|
- Webアプリの画像・動画ストレージ
|
||||||
|
- ログデータ保管
|
||||||
|
- オンプレS3互換ストレージ
|
||||||
|
|
||||||
|
### 主な機能
|
||||||
|
|
||||||
|
- S3 互換API対応
|
||||||
|
- Webベース管理コンソール
|
||||||
|
- IAM風のユーザー・ポリシー制御
|
||||||
|
- TLS対応(HTTPS通信)
|
||||||
|
- サーバーサイド暗号化(AES-256)
|
||||||
|
- マルチテナント対応(認証連携含む)
|
||||||
|
- オブジェクトのバージョン管理(設定時)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 実際の使い方
|
||||||
|
|
||||||
|
サンプルコードでは以下を作成しております
|
||||||
|
|
||||||
|
1. バケット作成 : sample-data(readwrite)
|
||||||
|
2. ユーザー設定 : appuser:password123
|
||||||
|
|
||||||
|
|
||||||
|
### Webコンソールを利用する場合
|
||||||
|
|
||||||
|
#### Webコンソールにアクセス
|
||||||
|
|
||||||
|
ブラウザで `http://<ホスト>:9001` にアクセス
|
||||||
|
管理者アカウントでログイン(`MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### バケット作成・ファイルアップロード
|
||||||
|
|
||||||
|
- Web UI からバケット作成
|
||||||
|
- ローカルからファイルをアップロード可能
|
||||||
|
- バケット単位で「パブリックアクセス許可」も可能
|
||||||
|
|
||||||
|
### `mc` コマンドによる操作
|
||||||
|
|
||||||
|
#### インストール
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://dl.min.io/client/mc/release/linux-amd64/mc
|
||||||
|
chmod +x mc
|
||||||
|
sudo mv mc /usr/local/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### MinIOへの接続
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mc alias set local http://localhost:9000 admin password123
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 操作例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mc mb local/mybucket
|
||||||
|
mc cp ./example.jpg local/mybucket/
|
||||||
|
mc ls local/mybucket
|
||||||
|
```
|
||||||
|
|
||||||
|
### 各プログラム言語のライブラリから利用する場合
|
||||||
|
|
||||||
|
MinIOはAmazon S3互換のAPIを提供しているためS3対応の
|
||||||
|
クライアントライブラリを使用すればPythonやNode.jsから
|
||||||
|
簡単に接続・操作できます
|
||||||
|
|
||||||
|
* Python: `boto3`
|
||||||
|
* Node.js: `@aws-sdk/client-s3`
|
||||||
|
|
||||||
|
**接続時の注意点**
|
||||||
|
* リージョン
|
||||||
|
* 仮に指定しますが実際は無視される
|
||||||
|
* バージョニング/ライフサイクル/通知
|
||||||
|
* 一部のS3機能はMinIOでサポートされていない、または挙動が違う場合あり
|
||||||
|
* 署名付きURL
|
||||||
|
* boto3などで生成する署名付きURLも動作しますが、MinIOの設定によって挙動が異なることがある
|
||||||
|
|
||||||
|
### minioライブラリ(MinIO公式)
|
||||||
|
|
||||||
|
MinIO専用に最適化されていて、軽量・シンプルで扱いやすいのが特徴です。
|
||||||
|
S3互換APIを使ってはいますがAmazon純正のSDK(@aws-sdk/*)と違いMinIOのユースケースに特化してます
|
||||||
|
|
||||||
|
#### インストール方法
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install minio
|
||||||
|
```
|
||||||
|
|
||||||
|
#### インストール方法
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 付録:Tips
|
||||||
|
|
||||||
|
- リバースプロキシ(Nginx)を通して HTTPS 公開することで、ブラウザ互換性やセキュリティを向上できる
|
||||||
|
- Cloudflare などのCDNと組み合わせれば、簡易的なグローバル配信も可能
|
||||||
|
- アプリからは AWS SDK を使ってアクセス可能(S3互換)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## まとめ
|
||||||
|
|
||||||
|
MinIO は S3互換かつ軽量で、開発・検証・軽量サービスにおけるストレージニーズに柔軟に対応できる。Docker環境でも素早く構築可能なため、内部ツールやバックアップ用途として導入検討する価値がある。
|
462
docs/plantuml.md
Normal file
462
docs/plantuml.md
Normal file
@ -0,0 +1,462 @@
|
|||||||
|
# 【PlantUML】概要及び基本的な使い方
|
||||||
|
|
||||||
|
- [【PlantUML】概要及び基本的な使い方](#plantuml概要及び基本的な使い方)
|
||||||
|
- [各図形の基本的な書き方](#各図形の基本的な書き方)
|
||||||
|
- [フローチャート図](#フローチャート図)
|
||||||
|
- [シーケンス図](#シーケンス図)
|
||||||
|
- [クラス図](#クラス図)
|
||||||
|
- [ER図](#er図)
|
||||||
|
- [リレーションシンボルの意味](#リレーションシンボルの意味)
|
||||||
|
- [アーキテクチャ図(簡易的な表現)](#アーキテクチャ図簡易的な表現)
|
||||||
|
- [WBS](#wbs)
|
||||||
|
- [業務フロー図](#業務フロー図)
|
||||||
|
- [スキンやスタイルの変更(PlantUML)](#スキンやスタイルの変更plantuml)
|
||||||
|
- [全体をグレースケールにする](#全体をグレースケールにする)
|
||||||
|
- [特定のアクティビティに色を付ける](#特定のアクティビティに色を付ける)
|
||||||
|
- [VSCodeでのPlantUMLスニペット設定](#vscodeでのplantumlスニペット設定)
|
||||||
|
- [関連リンク](#関連リンク)
|
||||||
|
|
||||||
|
|
||||||
|
## 各図形の基本的な書き方
|
||||||
|
|
||||||
|
### フローチャート図
|
||||||
|
|
||||||
|
````
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
start
|
||||||
|
:Christmas;
|
||||||
|
:Go shopping;
|
||||||
|
if (Let me think) then (One)
|
||||||
|
:Laptop;
|
||||||
|
elseif (Two)
|
||||||
|
:iPhone;
|
||||||
|
else (Three)
|
||||||
|
:Car;
|
||||||
|
endif
|
||||||
|
stop
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
start
|
||||||
|
:Christmas;
|
||||||
|
:Go shopping;
|
||||||
|
if (Let me think) then (One)
|
||||||
|
:Laptop;
|
||||||
|
elseif (Two)
|
||||||
|
:iPhone;
|
||||||
|
else (Three)
|
||||||
|
:Car;
|
||||||
|
endif
|
||||||
|
stop
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
### シーケンス図
|
||||||
|
|
||||||
|
````
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
actor User
|
||||||
|
participant Front
|
||||||
|
participant Server
|
||||||
|
|
||||||
|
User ->> Front : URLをリンクする
|
||||||
|
Front -->> User : 一覧画面を表示する
|
||||||
|
User ->> Front : 検索する
|
||||||
|
loop 対象商品
|
||||||
|
Front ->> Server : 商品情報を取得する
|
||||||
|
Server -->> Front : レスポンス
|
||||||
|
end
|
||||||
|
Front -->> User : 検索結果を表示する
|
||||||
|
note right of Front
|
||||||
|
Product Find\nsequence
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
actor User
|
||||||
|
participant Front
|
||||||
|
participant Server
|
||||||
|
|
||||||
|
User ->> Front : URLをリンクする
|
||||||
|
Front -->> User : 一覧画面を表示する
|
||||||
|
User ->> Front : 検索する
|
||||||
|
loop 対象商品
|
||||||
|
Front ->> Server : 商品情報を取得する
|
||||||
|
Server -->> Front : レスポンス
|
||||||
|
end
|
||||||
|
Front -->> User : 検索結果を表示する
|
||||||
|
note right of Front
|
||||||
|
Product Find\nsequence
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
APIのサンプル例
|
||||||
|
````
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
participant App
|
||||||
|
participant API
|
||||||
|
|
||||||
|
App ->> API: request
|
||||||
|
alt OK
|
||||||
|
API -->> App: 200
|
||||||
|
else error
|
||||||
|
API -->> App: 400
|
||||||
|
end
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
### クラス図
|
||||||
|
````
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
class Animal {
|
||||||
|
+int age
|
||||||
|
+String gender
|
||||||
|
+mate()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Duck {
|
||||||
|
+String beakColor
|
||||||
|
+swim()
|
||||||
|
+quack()
|
||||||
|
}
|
||||||
|
|
||||||
|
Animal <|-- Duck
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
class Animal {
|
||||||
|
+int age
|
||||||
|
+String gender
|
||||||
|
+mate()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Duck {
|
||||||
|
+String beakColor
|
||||||
|
+swim()
|
||||||
|
+quack()
|
||||||
|
}
|
||||||
|
|
||||||
|
Animal <|-- Duck
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
### ER図
|
||||||
|
|
||||||
|
データベース設計に特化したツールみたいには細かく設定ができない
|
||||||
|
|
||||||
|
* 関係性で1なのか0なのかなどはカーディナリティで表現できない
|
||||||
|
* 外部キーをfield単位で結びつけれない
|
||||||
|
|
||||||
|
````
|
||||||
|
## 基本的な構文
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
entity "エンティティ名" as 物理名 {
|
||||||
|
+ フィールド名 : データ型 <<制約>> -- 論理名(コメント)
|
||||||
|
}
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
#### リレーションシンボルの意味
|
||||||
|
|
||||||
|
* `||--||` : 1対1 (One to One)
|
||||||
|
* `}o--||` : 多対1 (Many to One)
|
||||||
|
* `o{--||` : 1対多 (One to Many)
|
||||||
|
* `}o--o{` : 多対多 (Many to Many)
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
entity "USER" as ユーザー {
|
||||||
|
+ user_id : int <<PK>> -- ユーザーID
|
||||||
|
--
|
||||||
|
name : string -- 名前
|
||||||
|
email : string -- メールアドレス
|
||||||
|
}
|
||||||
|
|
||||||
|
entity "ORDER" as 注文 {
|
||||||
|
+ order_id : int <<PK>> -- 注文ID
|
||||||
|
--
|
||||||
|
user_id : int <<FK>> -- ユーザーID (FK)
|
||||||
|
total_price : float -- 合計金額
|
||||||
|
order_date : date -- 注文日
|
||||||
|
}
|
||||||
|
|
||||||
|
ユーザー ||--o{ 注文 : "places"
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
### アーキテクチャ図(簡易的な表現)
|
||||||
|
````
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
package "Public" {
|
||||||
|
package "Private" {
|
||||||
|
[Server] --> [Database]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
package "Public" {
|
||||||
|
package "Private" {
|
||||||
|
[Server] --> [Database]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
(図形を読み込む)
|
||||||
|
|
||||||
|
https://github.com/davidholsgrove/gcp-icons-for-plantuml/tree/master/dist
|
||||||
|
|
||||||
|
````
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
!define GCPPuml https://raw.githubusercontent.com/davidholsgrove/gcp-icons-for-plantuml/master/dist
|
||||||
|
!includeurl GCPPuml/GCPCommon.puml
|
||||||
|
!includeurl GCPPuml/DeveloperTools/all.puml
|
||||||
|
!includeurl GCPPuml/Storage/CloudStorage.puml
|
||||||
|
!includeurl GCPPuml/Compute/CloudFunctions.puml
|
||||||
|
|
||||||
|
|
||||||
|
actor "Person" as personAlias
|
||||||
|
CloudToolsforVisualStudio(desktopAlias, "Label", "Technology", "Optional Description")
|
||||||
|
CloudStorage(storageAlias, "Label", "Technology", "Optional Description")
|
||||||
|
CloudFunctions(functionsAlias, "Label", "Function", "Optional Description")
|
||||||
|
|
||||||
|
personAlias --> desktopAlias
|
||||||
|
personAlias --> functionsAlias
|
||||||
|
desktopAlias --> storageAlias
|
||||||
|
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
!define GCPPuml https://raw.githubusercontent.com/davidholsgrove/gcp-icons-for-plantuml/master/dist
|
||||||
|
!includeurl GCPPuml/GCPCommon.puml
|
||||||
|
!includeurl GCPPuml/DeveloperTools/all.puml
|
||||||
|
!includeurl GCPPuml/Storage/CloudStorage.puml
|
||||||
|
!includeurl GCPPuml/Compute/CloudFunctions.puml
|
||||||
|
|
||||||
|
|
||||||
|
actor "Person" as personAlias
|
||||||
|
CloudToolsforVisualStudio(desktopAlias, "Label", "Technology", "Optional Description")
|
||||||
|
CloudStorage(storageAlias, "Label", "Technology", "Optional Description")
|
||||||
|
CloudFunctions(functionsAlias, "Label", "Function", "Optional Description")
|
||||||
|
|
||||||
|
personAlias --> desktopAlias
|
||||||
|
personAlias --> functionsAlias
|
||||||
|
desktopAlias --> storageAlias
|
||||||
|
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### WBS
|
||||||
|
|
||||||
|
````
|
||||||
|
```plantuml
|
||||||
|
@startwbs
|
||||||
|
* work
|
||||||
|
** work_A
|
||||||
|
*** 準備
|
||||||
|
*** 作業
|
||||||
|
*** リリース
|
||||||
|
** work_B
|
||||||
|
*** 準備
|
||||||
|
*** 作業_1
|
||||||
|
*** 作業_2
|
||||||
|
*** リリース
|
||||||
|
@endwbs
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startwbs
|
||||||
|
* work
|
||||||
|
** work_A
|
||||||
|
*** 準備
|
||||||
|
*** 作業
|
||||||
|
*** リリース
|
||||||
|
** work_B
|
||||||
|
*** 準備
|
||||||
|
*** 作業_1
|
||||||
|
*** 作業_2
|
||||||
|
*** リリース
|
||||||
|
@endwbs
|
||||||
|
```
|
||||||
|
### 業務フロー図
|
||||||
|
|
||||||
|
````
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
|__顧客__|
|
||||||
|
:注文する;
|
||||||
|
|
||||||
|
|#AntiqueWhite|__販売部門__|
|
||||||
|
:在庫を確認する;
|
||||||
|
:出荷を確認する;
|
||||||
|
|
||||||
|
|__出荷部門__|
|
||||||
|
:出荷する;
|
||||||
|
fork
|
||||||
|
:出荷を報告する;
|
||||||
|
|
||||||
|
|#AntiqueWhite|__経理部門__|
|
||||||
|
:請求する;
|
||||||
|
|
||||||
|
|__顧客__|
|
||||||
|
forkagain
|
||||||
|
:商品を受け取る;
|
||||||
|
|
||||||
|
|__顧客__|
|
||||||
|
end fork
|
||||||
|
:支払う;
|
||||||
|
|
||||||
|
|#AntiqueWhite|__経理部門__|
|
||||||
|
:入金を確認する;
|
||||||
|
|
||||||
|
@endum
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
|__顧客__|
|
||||||
|
:注文する;
|
||||||
|
|
||||||
|
|#AntiqueWhite|__販売部門__|
|
||||||
|
:在庫を確認する;
|
||||||
|
:出荷を確認する;
|
||||||
|
|
||||||
|
|__出荷部門__|
|
||||||
|
:出荷する;
|
||||||
|
fork
|
||||||
|
:出荷を報告する;
|
||||||
|
|
||||||
|
|#AntiqueWhite|__経理部門__|
|
||||||
|
:請求する;
|
||||||
|
|
||||||
|
|__顧客__|
|
||||||
|
forkagain
|
||||||
|
:商品を受け取る;
|
||||||
|
|
||||||
|
|__顧客__|
|
||||||
|
end fork
|
||||||
|
:支払う;
|
||||||
|
|
||||||
|
|#AntiqueWhite|__経理部門__|
|
||||||
|
:入金を確認する;
|
||||||
|
@endum
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## スキンやスタイルの変更(PlantUML)
|
||||||
|
|
||||||
|
PlantUMLではスキン(skinparam)でスタイルを制御します。
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
skinparam backgroundColor #EEEBDC
|
||||||
|
skinparam handwritten true
|
||||||
|
|
||||||
|
actor User
|
||||||
|
User -> System: Hello
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 全体をグレースケールにする
|
||||||
|
|
||||||
|
````
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
' 全体をグレースケールにする
|
||||||
|
skinparam monochrome true
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
skinparam monochrome true
|
||||||
|
|
||||||
|
actor User
|
||||||
|
User -> System: Hello
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 特定のアクティビティに色を付ける
|
||||||
|
|
||||||
|
```
|
||||||
|
'色を指定する
|
||||||
|
#ffccff:処理名;
|
||||||
|
|
||||||
|
'グラデーションの設定
|
||||||
|
#white-ffccff:処理名;
|
||||||
|
```
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
|
||||||
|
start
|
||||||
|
#ffccff :処理A;
|
||||||
|
#white-ffccff :処理B;
|
||||||
|
#aaffaa :処理C;
|
||||||
|
stop
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## VSCodeでのPlantUMLスニペット設定
|
||||||
|
|
||||||
|
1. [Ctrl] + [Shift] + [P]を押下
|
||||||
|
2. "Snippets: Configure Snippets" を選択
|
||||||
|
3. `plantuml.json`という名前で作成
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"PlantUML Sequence Diagram": {
|
||||||
|
"prefix": "plantuml:sequence",
|
||||||
|
"body": [
|
||||||
|
"@startuml",
|
||||||
|
"actor User",
|
||||||
|
"participant Front",
|
||||||
|
"participant Server",
|
||||||
|
"User ->> Front: Click Button",
|
||||||
|
"Front ->> Server: Request Data",
|
||||||
|
"Server -->> Front: Return Data",
|
||||||
|
"Front -->> User: Display Data",
|
||||||
|
"@enduml"
|
||||||
|
],
|
||||||
|
"description": "Create a sequence diagram"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 関連リンク
|
||||||
|
- [PlantUML 公式サイト](https://plantuml.com/)
|
||||||
|
- [PlantUML Online Editor](https://www.planttext.com/)
|
||||||
|
- [Visual Studio Code Extension](https://marketplace.visualstudio.com/items?itemName=jebbs.plantuml)
|
||||||
|
|
44
docs/trend2025.md
Normal file
44
docs/trend2025.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
## 空間コンピューティング / AR / XRの技術選定
|
||||||
|
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- index.html -->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Simple WebXR AR</title>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/three@0.152.2/examples/jsm/webxr/ARButton.js"></script>
|
||||||
|
</head>
|
||||||
|
<body style="margin: 0; overflow: hidden;">
|
||||||
|
<script type="module">
|
||||||
|
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.module.js';
|
||||||
|
import { ARButton } from 'https://cdn.jsdelivr.net/npm/three@0.152.2/examples/jsm/webxr/ARButton.js';
|
||||||
|
|
||||||
|
const scene = new THREE.Scene();
|
||||||
|
const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20);
|
||||||
|
|
||||||
|
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
renderer.xr.enabled = true;
|
||||||
|
document.body.appendChild(renderer.domElement);
|
||||||
|
document.body.appendChild(ARButton.createButton(renderer));
|
||||||
|
|
||||||
|
const geometry = new THREE.BoxGeometry(0.1, 0.1, 0.1);
|
||||||
|
const material = new THREE.MeshNormalMaterial();
|
||||||
|
const cube = new THREE.Mesh(geometry, material);
|
||||||
|
cube.position.set(0, 0, -0.5);
|
||||||
|
scene.add(cube);
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
renderer.setAnimationLoop(() => {
|
||||||
|
cube.rotation.y += 0.01;
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
animate();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
4
examles/sampleCommon.js
Normal file
4
examles/sampleCommon.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
const common = require('../src/lib/common.js');
|
||||||
|
|
||||||
|
console.log(common.formatDatetime(new Date(), 'yyyy/MM/dd hh:mm:ss'));
|
||||||
|
console.log(common.formatDatetime(new Date(), 'yyyy-MM-ddThh:mm:ss'));
|
17
examles/sampleCouchDB.js
Normal file
17
examles/sampleCouchDB.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
async function main() {
|
||||||
|
const {CouchDatabaseClient} = require('../src/classes/couchdb/CouchDatabaseClient');
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
protocol: 'http',
|
||||||
|
host: 'localhost',
|
||||||
|
port: 5984,
|
||||||
|
username: 'admin',
|
||||||
|
password: 'secret'
|
||||||
|
}
|
||||||
|
const client = new CouchDatabaseClient(config);
|
||||||
|
const users = client.getCollection('user');
|
||||||
|
|
||||||
|
const user = await users.create({name: 'testUser1', email: 'testUser1@example.com' });
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
25
examles/sampleMinio.js
Normal file
25
examles/sampleMinio.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
async function main() {
|
||||||
|
const MinioStorage = require('../src/classes/MinioStorage');
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
endPoint: 'localhost',
|
||||||
|
port: 9000,
|
||||||
|
useSSL: false,
|
||||||
|
accessKey: 'appuser',
|
||||||
|
secretKey: 'password123',
|
||||||
|
bucketName: 'sample-data'
|
||||||
|
}
|
||||||
|
|
||||||
|
const storage = new MinioStorage(config);
|
||||||
|
// ファイルを取得する
|
||||||
|
files = await storage.getFileList();
|
||||||
|
console.log(files);
|
||||||
|
|
||||||
|
// ファイルをアップロードする
|
||||||
|
await storage.uploadFile("./src/data/sample2.csv","sample2.csv");
|
||||||
|
files = await storage.getFileList();
|
||||||
|
console.log(files);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
632
package-lock.json
generated
632
package-lock.json
generated
@ -12,7 +12,9 @@
|
|||||||
"@google-cloud/storage": "^7.15.2",
|
"@google-cloud/storage": "^7.15.2",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"csv-writer": "^1.6.0",
|
"csv-writer": "^1.6.0",
|
||||||
"dotenv": "^16.4.7"
|
"dotenv": "^16.4.7",
|
||||||
|
"minio": "^8.0.5",
|
||||||
|
"nano": "^10.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jsdoc": "^4.0.4"
|
"jsdoc": "^4.0.4"
|
||||||
@ -232,6 +234,13 @@
|
|||||||
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
|
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@zxing/text-encoding": {
|
||||||
|
"version": "0.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz",
|
||||||
|
"integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==",
|
||||||
|
"license": "(Unlicense OR Apache-2.0)",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/abort-controller": {
|
"node_modules/abort-controller": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||||
@ -350,6 +359,47 @@
|
|||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/available-typed-arrays": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"possible-typed-array-names": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.8.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
|
||||||
|
"integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/axios/node_modules/form-data": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"es-set-tostringtag": "^2.1.0",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/b4a": {
|
"node_modules/b4a": {
|
||||||
"version": "1.6.7",
|
"version": "1.6.7",
|
||||||
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
|
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
|
||||||
@ -398,6 +448,29 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/block-stream2": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"readable-stream": "^3.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/block-stream2/node_modules/readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bluebird": {
|
"node_modules/bluebird": {
|
||||||
"version": "3.7.2",
|
"version": "3.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||||
@ -414,6 +487,12 @@
|
|||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/browser-or-node": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/buffer": {
|
"node_modules/buffer": {
|
||||||
"version": "6.0.3",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
@ -453,6 +532,24 @@
|
|||||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/call-bind": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.0",
|
||||||
|
"es-define-property": "^1.0.0",
|
||||||
|
"get-intrinsic": "^1.2.4",
|
||||||
|
"set-function-length": "^1.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/call-bind-apply-helpers": {
|
"node_modules/call-bind-apply-helpers": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
@ -466,6 +563,22 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/call-bound": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
|
"get-intrinsic": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/catharsis": {
|
"node_modules/catharsis": {
|
||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
|
||||||
@ -593,6 +706,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/decode-uri-component": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/define-data-property": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-define-property": "^1.0.0",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"gopd": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/delayed-stream": {
|
"node_modules/delayed-stream": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
@ -761,6 +900,12 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eventemitter3": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/events": {
|
"node_modules/events": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||||
@ -800,6 +945,50 @@
|
|||||||
"fxparser": "src/cli/cli.js"
|
"fxparser": "src/cli/cli.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/filter-obj": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||||
|
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/for-each": {
|
||||||
|
"version": "0.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||||
|
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-callable": "^1.2.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/foreground-child": {
|
"node_modules/foreground-child": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||||
@ -998,6 +1187,18 @@
|
|||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/has-property-descriptors": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-define-property": "^1.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/has-symbols": {
|
"node_modules/has-symbols": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||||
@ -1118,6 +1319,43 @@
|
|||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/ipaddr.js": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-arguments": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bound": "^1.0.2",
|
||||||
|
"has-tostringtag": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-callable": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-fullwidth-code-point": {
|
"node_modules/is-fullwidth-code-point": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
@ -1127,6 +1365,42 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-generator-function": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bound": "^1.0.3",
|
||||||
|
"get-proto": "^1.0.0",
|
||||||
|
"has-tostringtag": "^1.0.2",
|
||||||
|
"safe-regex-test": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-regex": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bound": "^1.0.2",
|
||||||
|
"gopd": "^1.2.0",
|
||||||
|
"has-tostringtag": "^1.0.2",
|
||||||
|
"hasown": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-stream": {
|
"node_modules/is-stream": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||||
@ -1139,6 +1413,21 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-typed-array": {
|
||||||
|
"version": "1.1.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
|
||||||
|
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"which-typed-array": "^1.1.16"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/isarray": {
|
"node_modules/isarray": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
@ -1416,6 +1705,31 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minio": {
|
||||||
|
"version": "8.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/minio/-/minio-8.0.5.tgz",
|
||||||
|
"integrity": "sha512-/vAze1uyrK2R/DSkVutE4cjVoAowvIQ18RAwn7HrqnLecLlMazFnY0oNBqfuoAWvu7mZIGX75AzpuV05TJeoHg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"async": "^3.2.4",
|
||||||
|
"block-stream2": "^2.1.0",
|
||||||
|
"browser-or-node": "^2.1.1",
|
||||||
|
"buffer-crc32": "^1.0.0",
|
||||||
|
"eventemitter3": "^5.0.1",
|
||||||
|
"fast-xml-parser": "^4.4.1",
|
||||||
|
"ipaddr.js": "^2.0.1",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"mime-types": "^2.1.35",
|
||||||
|
"query-string": "^7.1.3",
|
||||||
|
"stream-json": "^1.8.0",
|
||||||
|
"through2": "^4.0.2",
|
||||||
|
"web-encoding": "^1.1.5",
|
||||||
|
"xml2js": "^0.5.0 || ^0.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^16 || ^18 || >=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/minipass": {
|
"node_modules/minipass": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||||
@ -1444,6 +1758,26 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/nano": {
|
||||||
|
"version": "10.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/nano/-/nano-10.1.4.tgz",
|
||||||
|
"integrity": "sha512-bJOFIPLExIbF6mljnfExXX9Cub4W0puhDjVMp+qV40xl/DBvgKao7St4+6/GB6EoHZap7eFnrnx4mnp5KYgwJA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.4",
|
||||||
|
"node-abort-controller": "^3.1.1",
|
||||||
|
"qs": "^6.13.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-abort-controller": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
@ -1473,6 +1807,18 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/object-inspect": {
|
||||||
|
"version": "1.13.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
|
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/once": {
|
"node_modules/once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
@ -1528,6 +1874,15 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/possible-typed-array-names": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/process": {
|
"node_modules/process": {
|
||||||
"version": "0.11.10",
|
"version": "0.11.10",
|
||||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
@ -1543,6 +1898,12 @@
|
|||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/punycode.js": {
|
"node_modules/punycode.js": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
||||||
@ -1553,6 +1914,39 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/qs": {
|
||||||
|
"version": "6.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||||
|
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"side-channel": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/query-string": {
|
||||||
|
"version": "7.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
|
||||||
|
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"decode-uri-component": "^0.2.2",
|
||||||
|
"filter-obj": "^1.1.0",
|
||||||
|
"split-on-first": "^1.0.0",
|
||||||
|
"strict-uri-encode": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/readable-stream": {
|
"node_modules/readable-stream": {
|
||||||
"version": "4.7.0",
|
"version": "4.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
|
||||||
@ -1643,6 +2037,46 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-regex-test": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bound": "^1.0.2",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"is-regex": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sax": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/set-function-length": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"define-data-property": "^1.1.4",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"function-bind": "^1.1.2",
|
||||||
|
"get-intrinsic": "^1.2.4",
|
||||||
|
"gopd": "^1.0.1",
|
||||||
|
"has-property-descriptors": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
@ -1664,6 +2098,78 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/side-channel": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"object-inspect": "^1.13.3",
|
||||||
|
"side-channel-list": "^1.0.0",
|
||||||
|
"side-channel-map": "^1.0.1",
|
||||||
|
"side-channel-weakmap": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/side-channel-list": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"object-inspect": "^1.13.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/side-channel-map": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bound": "^1.0.2",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"get-intrinsic": "^1.2.5",
|
||||||
|
"object-inspect": "^1.13.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/side-channel-weakmap": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bound": "^1.0.2",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"get-intrinsic": "^1.2.5",
|
||||||
|
"object-inspect": "^1.13.3",
|
||||||
|
"side-channel-map": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/signal-exit": {
|
"node_modules/signal-exit": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||||
@ -1676,6 +2182,21 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/split-on-first": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/stream-chain": {
|
||||||
|
"version": "2.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz",
|
||||||
|
"integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/stream-events": {
|
"node_modules/stream-events": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
|
||||||
@ -1685,6 +2206,15 @@
|
|||||||
"stubs": "^3.0.0"
|
"stubs": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/stream-json": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"stream-chain": "^2.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/stream-shift": {
|
"node_modules/stream-shift": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
|
||||||
@ -1704,6 +2234,15 @@
|
|||||||
"bare-events": "^2.2.0"
|
"bare-events": "^2.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/strict-uri-encode": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string_decoder": {
|
"node_modules/string_decoder": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
@ -1914,6 +2453,29 @@
|
|||||||
"b4a": "^1.6.4"
|
"b4a": "^1.6.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/through2": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"readable-stream": "3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/through2/node_modules/readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tr46": {
|
"node_modules/tr46": {
|
||||||
"version": "0.0.3",
|
"version": "0.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
@ -1940,6 +2502,19 @@
|
|||||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/util": {
|
||||||
|
"version": "0.12.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
|
||||||
|
"integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"is-arguments": "^1.0.4",
|
||||||
|
"is-generator-function": "^1.0.7",
|
||||||
|
"is-typed-array": "^1.1.3",
|
||||||
|
"which-typed-array": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
@ -1955,6 +2530,18 @@
|
|||||||
"uuid": "dist/bin/uuid"
|
"uuid": "dist/bin/uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/web-encoding": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"util": "^0.12.3"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@zxing/text-encoding": "0.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/webidl-conversions": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
@ -1986,6 +2573,27 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/which-typed-array": {
|
||||||
|
"version": "1.1.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
|
||||||
|
"integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"available-typed-arrays": "^1.0.7",
|
||||||
|
"call-bind": "^1.0.8",
|
||||||
|
"call-bound": "^1.0.4",
|
||||||
|
"for-each": "^0.3.5",
|
||||||
|
"get-proto": "^1.0.1",
|
||||||
|
"gopd": "^1.2.0",
|
||||||
|
"has-tostringtag": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/wrap-ansi": {
|
"node_modules/wrap-ansi": {
|
||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||||
@ -2083,6 +2691,28 @@
|
|||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/xml2js": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~11.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xmlbuilder": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/xmlcreate": {
|
"node_modules/xmlcreate": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz",
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
"@google-cloud/storage": "^7.15.2",
|
"@google-cloud/storage": "^7.15.2",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"csv-writer": "^1.6.0",
|
"csv-writer": "^1.6.0",
|
||||||
"dotenv": "^16.4.7"
|
"dotenv": "^16.4.7",
|
||||||
|
"minio": "^8.0.5",
|
||||||
|
"nano": "^10.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jsdoc": "^4.0.4"
|
"jsdoc": "^4.0.4"
|
||||||
|
115
src/classes/MinioStorage.js
Normal file
115
src/classes/MinioStorage.js
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// @ts-check を付けると型チェックが有効に!
|
||||||
|
/** @ts-check */
|
||||||
|
|
||||||
|
// 型定義をインポート
|
||||||
|
const Minio = require('minio');
|
||||||
|
// enum を使っている場合
|
||||||
|
const { ProcessType } = require('../types/IStorage');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
|
||||||
|
class MinioStorage {
|
||||||
|
constructor(config) {
|
||||||
|
this.minioClient = new Minio.Client({
|
||||||
|
endPoint: config.endPoint,
|
||||||
|
port: config.port,
|
||||||
|
useSSL: config.useSSL,
|
||||||
|
accessKey: config.accessKey,
|
||||||
|
secretKey: config.secretKey
|
||||||
|
});
|
||||||
|
this.bucketName = config.bucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFileList(prefix = '') {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const objectKeys = [];
|
||||||
|
const stream = this.minioClient.listObjects(this.bucketName, prefix, true);
|
||||||
|
stream.on('data', obj => objectKeys.push(obj.name));
|
||||||
|
stream.on('end', () => resolve(objectKeys));
|
||||||
|
stream.on('error', err => reject(err));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async uploadFile(filePath, objectKey) {
|
||||||
|
return this.minioClient.fPutObject(this.bucketName, objectKey, filePath)
|
||||||
|
.then(() => {
|
||||||
|
console.log('File uploaded successfully.');
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('File upload error:', err);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オブジェクトをファイルとして保存(ローカル)
|
||||||
|
* @param {string} objectKey 取得するオブジェクトキー
|
||||||
|
* @param {string} destinationPath 保存先ファイルパス
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async downloadFile(objectKey, destinationPath) {
|
||||||
|
const dataStream = await this.minioClient.getObject(this.bucketName, objectKey);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const fileStream = fs.createWriteStream(destinationPath);
|
||||||
|
dataStream.pipe(fileStream);
|
||||||
|
dataStream.on('end', () => {
|
||||||
|
console.log('File Download File end');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
dataStream.on('error', reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オブジェクトをメモリ上に読み込む(Buffer)
|
||||||
|
* @param {string} objectKey 取得するオブジェクトキー
|
||||||
|
* @returns {Promise<Buffer>}
|
||||||
|
*/
|
||||||
|
async downloadContents(objectKey) {
|
||||||
|
const dataStream = await this.minioClient.getObject(this.bucketName, objectKey);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const chunks = [];
|
||||||
|
dataStream.on('data', chunk => chunks.push(chunk));
|
||||||
|
dataStream.on('end', () => resolve(Buffer.concat(chunks)));
|
||||||
|
dataStream.on('error', reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 単一オブジェクトを削除する
|
||||||
|
* @param {string} objectKey 削除するオブジェクトキー
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async delete(objectKey) {
|
||||||
|
await this.minioClient.removeObject(this.bucketName, objectKey);
|
||||||
|
console.log('File deleted successfully.');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 署名付きURLを生成する(GET or PUT)
|
||||||
|
* @param {string} objectKey 対象のオブジェクトキー
|
||||||
|
* @param {number} processType 処理タイプ(0=GET, 1=PUT)
|
||||||
|
* @param {{ expiresIn: number }} options 有効期限(秒)
|
||||||
|
* @returns {Promise<string>} 発行された署名付きURL
|
||||||
|
*/
|
||||||
|
async getSignedUrl(objectKey, processType, options) {
|
||||||
|
if (!options.expiresIn) {
|
||||||
|
options.expiresIn = 60 * 60; // 1 hours
|
||||||
|
}
|
||||||
|
switch (processType) {
|
||||||
|
case ProcessType.GET:
|
||||||
|
return this.minioClient.presignedGetObject(this.bucketName, objectKey, options.expiresIn);
|
||||||
|
case ProcessType.PUT:
|
||||||
|
return this.minioClient.presignedPutObject(this.bucketName, objectKey, options.expiresIn);
|
||||||
|
default:
|
||||||
|
throw new Error('Invalid process type.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = MinioStorage;
|
97
src/classes/couchdb/CouchCollection.js
Normal file
97
src/classes/couchdb/CouchCollection.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// couchdb/CouchCollection.js
|
||||||
|
const { CouchDocument } = require('./CouchDocument.js');
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
*/
|
||||||
|
class CouchCollection {
|
||||||
|
/**
|
||||||
|
* @param {import('nano').DocumentScope<any>} db
|
||||||
|
*/
|
||||||
|
constructor(db) {
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} id
|
||||||
|
* @returns {Promise<import('../interfaces').IDocument<T> | null>}
|
||||||
|
*/
|
||||||
|
async get(id) {
|
||||||
|
try {
|
||||||
|
const doc = await this.db.get(id);
|
||||||
|
return new CouchDocument(
|
||||||
|
doc._id,
|
||||||
|
doc.data,
|
||||||
|
doc.createdAt ? new Date(doc.createdAt) : undefined,
|
||||||
|
doc.updatedAt ? new Date(doc.updatedAt) : undefined,
|
||||||
|
doc._rev
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
if (err.statusCode === 404) return null;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Promise<Array<import('../interfaces').IDocument<T>>>}
|
||||||
|
*/
|
||||||
|
async list() {
|
||||||
|
const result = await this.db.list({ include_docs: true });
|
||||||
|
return result.rows
|
||||||
|
.filter(row => row.doc)
|
||||||
|
.map(row =>
|
||||||
|
new CouchDocument(
|
||||||
|
row.doc._id,
|
||||||
|
row.doc.data,
|
||||||
|
row.doc.createdAt ? new Date(row.doc.createdAt) : undefined,
|
||||||
|
row.doc.updatedAt ? new Date(row.doc.updatedAt) : undefined,
|
||||||
|
row.doc._rev
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {T} data
|
||||||
|
* @returns {Promise<import('../interfaces').IDocument<T>>}
|
||||||
|
*/
|
||||||
|
async create(data) {
|
||||||
|
const now = new Date();
|
||||||
|
const doc = {
|
||||||
|
data,
|
||||||
|
createdAt: now.toISOString(),
|
||||||
|
updatedAt: now.toISOString(),
|
||||||
|
};
|
||||||
|
const response = await this.db.insert(doc);
|
||||||
|
return new CouchDocument(response.id, data, now, now, response.rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} id
|
||||||
|
* @param {Partial<T>} data
|
||||||
|
* @returns {Promise<import('../interfaces').IDocument<T>>}
|
||||||
|
*/
|
||||||
|
async update(id, data) {
|
||||||
|
const existing = await this.db.get(id);
|
||||||
|
const updated = {
|
||||||
|
...existing,
|
||||||
|
data: {
|
||||||
|
...existing.data,
|
||||||
|
...data,
|
||||||
|
},
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
const response = await this.db.insert(updated);
|
||||||
|
return new CouchDocument(response.id, updated.data, existing.createdAt, new Date(), response.rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} id
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async delete(id) {
|
||||||
|
const doc = await this.db.get(id);
|
||||||
|
await this.db.destroy(id, doc._rev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = { CouchCollection };
|
40
src/classes/couchdb/CouchDatabaseClient.js
Normal file
40
src/classes/couchdb/CouchDatabaseClient.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// couchdb/CouchDatabaseClient.js
|
||||||
|
const nano = require('nano');
|
||||||
|
const { CouchCollection } = require('./CouchCollection.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @typedef {import('../interfaces').ICollection<T>} ICollection
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} CouchDBConfig
|
||||||
|
* @property {string} protocol
|
||||||
|
* @property {string} host
|
||||||
|
* @property {number} port
|
||||||
|
* @property {string} username
|
||||||
|
* @property {string} password
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CouchDatabaseClient {
|
||||||
|
/**
|
||||||
|
* @param {CouchDBConfig} config
|
||||||
|
*/
|
||||||
|
constructor(config) {
|
||||||
|
const { protocol, host, port, username, password } = config;
|
||||||
|
const url = `${protocol}://${encodeURIComponent(username)}:${encodeURIComponent(password)}@${host}:${port}`;
|
||||||
|
this.server = nano(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {ICollection<T>}
|
||||||
|
*/
|
||||||
|
getCollection(name) {
|
||||||
|
const db = this.server.db.use(name);
|
||||||
|
return new CouchCollection(db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { CouchDatabaseClient };
|
34
src/classes/couchdb/CouchDocument.js
Normal file
34
src/classes/couchdb/CouchDocument.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// couchdb/CouchDocument.js
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @typedef {Object} IDocument
|
||||||
|
* @property {string} id
|
||||||
|
* @property {T} data
|
||||||
|
* @property {Date=} createdAt
|
||||||
|
* @property {Date=} updatedAt
|
||||||
|
* @property {string|number=} version
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @implements {IDocument<T>}
|
||||||
|
*/
|
||||||
|
class CouchDocument {
|
||||||
|
/**
|
||||||
|
* @param {string} id
|
||||||
|
* @param {T} data
|
||||||
|
* @param {Date=} createdAt
|
||||||
|
* @param {Date=} updatedAt
|
||||||
|
* @param {string|number=} version
|
||||||
|
*/
|
||||||
|
constructor(id, data, createdAt, updatedAt, version) {
|
||||||
|
this.id = id;
|
||||||
|
this.data = data;
|
||||||
|
this.createdAt = createdAt;
|
||||||
|
this.updatedAt = updatedAt;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { CouchDocument };
|
11
src/data/sample1.csv
Normal file
11
src/data/sample1.csv
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
ID,Name,Age,Email
|
||||||
|
1,User_1,26,user1@example.com
|
||||||
|
2,User_2,65,user2@example.com
|
||||||
|
3,User_3,39,user3@example.com
|
||||||
|
4,User_4,51,user4@example.com
|
||||||
|
5,User_5,25,user5@example.com
|
||||||
|
6,User_6,26,user6@example.com
|
||||||
|
7,User_7,69,user7@example.com
|
||||||
|
8,User_8,47,user8@example.com
|
||||||
|
9,User_9,40,user9@example.com
|
||||||
|
10,User_10,28,user10@example.com
|
|
11
src/data/sample2.csv
Normal file
11
src/data/sample2.csv
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
ID,Name,Age,Email
|
||||||
|
1,User_1,63,user1@example.com
|
||||||
|
2,User_2,46,user2@example.com
|
||||||
|
3,User_3,69,user3@example.com
|
||||||
|
4,User_4,62,user4@example.com
|
||||||
|
5,User_5,64,user5@example.com
|
||||||
|
6,User_6,52,user6@example.com
|
||||||
|
7,User_7,41,user7@example.com
|
||||||
|
8,User_8,64,user8@example.com
|
||||||
|
9,User_9,22,user9@example.com
|
||||||
|
10,User_10,62,user10@example.com
|
|
42
src/front/ar/WebXR.html
Normal file
42
src/front/ar/WebXR.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<!-- index.html -->
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Simple WebXR AR</title>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/three@0.152.2/examples/jsm/webxr/ARButton.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body style="margin: 0; overflow: hidden;">
|
||||||
|
<script type="module">
|
||||||
|
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.module.js';
|
||||||
|
import { ARButton } from 'https://cdn.jsdelivr.net/npm/three@0.152.2/examples/jsm/webxr/ARButton.js';
|
||||||
|
|
||||||
|
const scene = new THREE.Scene();
|
||||||
|
const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20);
|
||||||
|
|
||||||
|
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
renderer.xr.enabled = true;
|
||||||
|
document.body.appendChild(renderer.domElement);
|
||||||
|
document.body.appendChild(ARButton.createButton(renderer));
|
||||||
|
|
||||||
|
const geometry = new THREE.BoxGeometry(0.1, 0.1, 0.1);
|
||||||
|
const material = new THREE.MeshNormalMaterial();
|
||||||
|
const cube = new THREE.Mesh(geometry, material);
|
||||||
|
cube.position.set(0, 0, -0.5);
|
||||||
|
scene.add(cube);
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
renderer.setAnimationLoop(() => {
|
||||||
|
cube.rotation.y += 0.01;
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
animate();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
74
src/front/ar/WebXRCamera.html
Normal file
74
src/front/ar/WebXRCamera.html
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>WebXR AR Sample</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<script type="module">
|
||||||
|
import * as THREE from 'https://cdn.skypack.dev/three@0.152.2';
|
||||||
|
import { ARButton } from 'https://cdn.skypack.dev/three@0.152.2/examples/jsm/webxr/ARButton.js';
|
||||||
|
|
||||||
|
let camera, scene, renderer;
|
||||||
|
let controller;
|
||||||
|
|
||||||
|
init();
|
||||||
|
animate();
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
// Renderer
|
||||||
|
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||||
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
renderer.xr.enabled = true;
|
||||||
|
document.body.appendChild(renderer.domElement);
|
||||||
|
|
||||||
|
// ARボタンを追加(Hit-test機能必要)
|
||||||
|
document.body.appendChild(
|
||||||
|
ARButton.createButton(renderer, { requiredFeatures: ['hit-test'] })
|
||||||
|
);
|
||||||
|
|
||||||
|
// SceneとCamera
|
||||||
|
scene = new THREE.Scene();
|
||||||
|
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20);
|
||||||
|
|
||||||
|
// 光源
|
||||||
|
const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 1);
|
||||||
|
light.position.set(0.5, 1, 0.25);
|
||||||
|
scene.add(light);
|
||||||
|
|
||||||
|
// 立方体を作成
|
||||||
|
const geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
|
||||||
|
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
|
||||||
|
const cube = new THREE.Mesh(geometry, material);
|
||||||
|
cube.position.set(0, 0, -0.5); // カメラの前に表示
|
||||||
|
scene.add(cube);
|
||||||
|
|
||||||
|
// コントローラ
|
||||||
|
controller = renderer.xr.getController(0);
|
||||||
|
scene.add(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
renderer.setAnimationLoop(render);
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
48
src/front/ar/afreame/aframe-hover.html
Normal file
48
src/front/ar/afreame/aframe-hover.html
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>ホバーで説明表示</title>
|
||||||
|
<script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<a-scene cursor="rayOrigin: mouse" raycaster="objects: .hoverable">
|
||||||
|
<!-- 地面 -->
|
||||||
|
<a-plane position="0 0 0" rotation="-90 0 0" width="50" height="50" color="#7BC8A4"></a-plane>
|
||||||
|
|
||||||
|
<!-- カメラ -->
|
||||||
|
<a-entity wasd-controls camera look-controls position="0 1.6 5"></a-entity>
|
||||||
|
|
||||||
|
<!-- 展示物 -->
|
||||||
|
<a-box id="exhibit" class="hoverable" position="0 0.5 -5" color="#4CC3D9" depth="1" height="1" width="1"></a-box>
|
||||||
|
|
||||||
|
<!-- 説明テキスト(最初は非表示) -->
|
||||||
|
<a-text id="tooltip" value="これは展示物です" visible="false"
|
||||||
|
position="0 1.5 -5" color="#000" align="center" width="4">
|
||||||
|
</a-text>
|
||||||
|
|
||||||
|
<!-- イベントスクリプト -->
|
||||||
|
<script>
|
||||||
|
AFRAME.registerComponent('show-tooltip', {
|
||||||
|
init: function () {
|
||||||
|
const tooltip = document.querySelector('#tooltip');
|
||||||
|
this.el.addEventListener('mouseenter', () => {
|
||||||
|
tooltip.setAttribute('visible', 'true');
|
||||||
|
});
|
||||||
|
this.el.addEventListener('mouseleave', () => {
|
||||||
|
tooltip.setAttribute('visible', 'false');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const exhibit = document.querySelector('#exhibit');
|
||||||
|
exhibit.setAttribute('show-tooltip', '');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</a-scene>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
44
src/front/ar/afreame/aframe-three.html
Normal file
44
src/front/ar/afreame/aframe-three.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>AR.js マーカーベース サンプル</title>
|
||||||
|
<script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/gh/AR-js-org/AR.js@3.3.2/aframe/build/aframe-ar.min.js"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#info {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
color: white;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="info">「Hiroマーカー」をカメラにかざしてください</div>
|
||||||
|
|
||||||
|
<a-scene embedded arjs="sourceType: webcam; debugUIEnabled: false;">
|
||||||
|
<!-- マーカーが認識されたときに表示される青い箱 -->
|
||||||
|
<a-marker preset="hiro">
|
||||||
|
<a-box position="0 0.5 0" material="color: blue;" shadow></a-box>
|
||||||
|
<a-text value="Hello AR!" position="-0.5 1 0" color="white"></a-text>
|
||||||
|
</a-marker>
|
||||||
|
|
||||||
|
<!-- カメラ -->
|
||||||
|
<a-entity camera></a-entity>
|
||||||
|
</a-scene>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
F
|
21
src/front/ar/afreame/aframe.html
Normal file
21
src/front/ar/afreame/aframe.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>歩行型Webページ</title>
|
||||||
|
<script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a-scene>
|
||||||
|
<!-- 地面 -->
|
||||||
|
<a-plane position="0 0 0" rotation="-90 0 0" width="50" height="50" color="#7BC8A4"></a-plane>
|
||||||
|
|
||||||
|
<!-- カメラとコントロール(WASDキーで歩行可能) -->
|
||||||
|
<a-entity camera wasd-controls look-controls position="0 1.6 5"></a-entity>
|
||||||
|
|
||||||
|
<!-- 建物風のオブジェクト -->
|
||||||
|
<a-box position="0 0.5 -5" depth="5" height="1" width="5" color="#4CC3D9"></a-box>
|
||||||
|
<a-box position="10 0.5 -5" depth="5" height="1" width="5" color="#FFC65D"></a-box>
|
||||||
|
<a-sphere position="-10 1 -5" radius="1" color="#EF2D5E"></a-sphere>
|
||||||
|
</a-scene>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* @description downloadボタンを押下したらCSVファイルをzip圧縮してダウンロードする
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// ダウンロードボタン
|
||||||
|
const downloadButton = document.getElementById('download-button');
|
||||||
|
const downloadButtonBase64 = document.getElementById('download-button-base64');
|
||||||
|
|
||||||
|
// ダウンロードボタンを押下した時の処理を記載する
|
||||||
|
downloadButton.addEventListener('click', async () => {
|
||||||
|
// URLを別のウィンドウで開く
|
||||||
|
// GCSからZIPファイルのコンテンツが返る
|
||||||
|
window.open('http://localhost:3000/downdload-gcs');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
downloadButtonBase64.addEventListener('click', async () => {
|
||||||
|
// ダウンロードボタンを無効化する
|
||||||
|
downloadButtonBase64.disabled = true;
|
||||||
|
try {
|
||||||
|
// localhost:3000/downdload-gcs-json にリクエストを送る
|
||||||
|
const response = await fetch('http://localhost:3000/downdload-gcs-json');
|
||||||
|
if (!response.ok ) throw new Error('Failed to fetch');
|
||||||
|
// JSONでdataにbase64が返ってくる
|
||||||
|
const res = await response.json();
|
||||||
|
const base64 = res.data;
|
||||||
|
|
||||||
|
// Base64をデコードしてZIPファイルを生成
|
||||||
|
const zipBlob = await fetch(`data:application/zip;base64,${base64}`).then(res => res.blob());
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = URL.createObjectURL(zipBlob);
|
||||||
|
a.download = "serverFile.zip";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
} catch (e) {
|
||||||
|
// エラーが発生した場合
|
||||||
|
console.error(e);
|
||||||
|
alert('ダウンロードに失敗しました');
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// ダウンロードボタンを有効化する
|
||||||
|
downloadButtonBase64.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
@ -9,7 +9,9 @@
|
|||||||
<body>
|
<body>
|
||||||
<h1>JavaScript Develop Download To Google Cloud Storage</h1>
|
<h1>JavaScript Develop Download To Google Cloud Storage</h1>
|
||||||
<p >Zip File Download</p>
|
<p >Zip File Download</p>
|
||||||
|
|
||||||
<button id="download-button">Zip Download(GCS)</button>
|
<button id="download-button">Zip Download(GCS)</button>
|
||||||
|
<button id="download-button-base64">Zip Download(GCS JSON Base64)</button>
|
||||||
<script src="assets/cloud-storage.js"></script>
|
<script src="assets/cloud-storage.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -0,0 +1,22 @@
|
|||||||
|
const common = {
|
||||||
|
/**
|
||||||
|
* 日時データを指定フォーマットで文字列化する
|
||||||
|
* @param {Date} dt
|
||||||
|
* @param {String} fmt
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
formatDatetime(dt,fmt="yyyy-MM-dd hh:mm:ss"){
|
||||||
|
const pad = (n) => n < 10 ? '0' + n : n;
|
||||||
|
const replacements = {
|
||||||
|
"yyyy": dt.getFullYear(),
|
||||||
|
"MM": pad(dt.getMonth() + 1),
|
||||||
|
"dd": pad(dt.getDate()),
|
||||||
|
"hh": pad(dt.getHours()),
|
||||||
|
"mm": pad(dt.getMinutes()),
|
||||||
|
"ss": pad(dt.getSeconds())
|
||||||
|
};
|
||||||
|
return fmt.replace(/yyyy|MM|dd|hh|mm|ss/g, (match) => replacements[match]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = common;
|
175
src/script/couchDB/README.md
Normal file
175
src/script/couchDB/README.md
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
# [NoSQL][CouchDB]Node.jsの利用ガイド(nanoライブラリ)
|
||||||
|
|
||||||
|
CouchDBはドキュメント指向のNoSQLデータベースで、
|
||||||
|
HTTPベースのRESTful APIで操作可能です。
|
||||||
|
|
||||||
|
Node.jsでは公式の軽量クライアントであるnanoを使うことで
|
||||||
|
簡単にCouchDBとやりとりできます。
|
||||||
|
|
||||||
|
[戻る](https://wiki.pglikers.com/e/en/private/db/nosql/couch-db)
|
||||||
|
|
||||||
|
|
||||||
|
## nanoのインストール
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install nano
|
||||||
|
```
|
||||||
|
|
||||||
|
nanoはNode.js向けに設計されているため
|
||||||
|
ブラウザ(フロントエンド)では使用できません。
|
||||||
|
|
||||||
|
## CouchDBとの接続
|
||||||
|
|
||||||
|
```sh
|
||||||
|
const nano = require('nano')('http://admin:secret@localhost:5984');
|
||||||
|
```
|
||||||
|
|
||||||
|
**envなどを活用して設定する場合**
|
||||||
|
|
||||||
|
```conf
|
||||||
|
COUCHDB_HOST=localhost
|
||||||
|
COUCHDB_PORT=5984
|
||||||
|
COUCHDB_USER=admin
|
||||||
|
COUCHDB_PASSWORD=secret
|
||||||
|
```
|
||||||
|
|
||||||
|
サンプルコード
|
||||||
|
|
||||||
|
```js
|
||||||
|
require('dotenv').config();
|
||||||
|
const protocol = 'http'; // or https
|
||||||
|
const {
|
||||||
|
COUCHDB_HOST,
|
||||||
|
COUCHDB_PORT,
|
||||||
|
COUCHDB_USER,
|
||||||
|
COUCHDB_PASSWORD
|
||||||
|
} = process.env;
|
||||||
|
const url = `${protocol}://${COUCHDB_USER}:${COUCHDB_PASSWORD}@${COUCHDB_HOST}:${COUCHDB_PORT}`;
|
||||||
|
const nano = require('nano')(url);
|
||||||
|
```
|
||||||
|
|
||||||
|
## CouchDBの使い方
|
||||||
|
|
||||||
|
### データベースの操作
|
||||||
|
|
||||||
|
```js
|
||||||
|
// データベース作成
|
||||||
|
await nano.db.create('user');
|
||||||
|
|
||||||
|
// データベース削除
|
||||||
|
await nano.db.destroy('user');
|
||||||
|
|
||||||
|
// 一覧取得
|
||||||
|
const dbs = await nano.db.list();
|
||||||
|
console.log(dbs);
|
||||||
|
```
|
||||||
|
|
||||||
|
### ドキュメントの操作
|
||||||
|
|
||||||
|
```js
|
||||||
|
const db = nano.db.use('user');
|
||||||
|
|
||||||
|
// 作成
|
||||||
|
await db.insert({ name: 'Alice', email: 'alice@example.com' });
|
||||||
|
|
||||||
|
// 取得
|
||||||
|
const doc = await db.get('document_id');
|
||||||
|
|
||||||
|
// 更新
|
||||||
|
doc.age = 30;
|
||||||
|
await db.insert(doc);
|
||||||
|
|
||||||
|
// 削除
|
||||||
|
await db.destroy(doc._id, doc._rev);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### インデックス作成(パフォーマンス向上)
|
||||||
|
|
||||||
|
Mangoクエリ(find)はインデックスがあると高速になります。
|
||||||
|
例としてユーザーの年齢でよく検索するなら、
|
||||||
|
事前に下記のようにインデックスを作ることを推奨します
|
||||||
|
|
||||||
|
```js
|
||||||
|
await db.createIndex({
|
||||||
|
index: { fields: ['age'] },
|
||||||
|
name: 'age-index',
|
||||||
|
type: 'json'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
### nanoの初期化オプション
|
||||||
|
|
||||||
|
```js
|
||||||
|
const nano = require('nano')({
|
||||||
|
url: 'http://localhost:5984',
|
||||||
|
requestDefaults: {
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Basic ...' // 任意のカスタムヘッダー
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 検索のサンプル
|
||||||
|
|
||||||
|
#### 名前が'A'から始まる人だけ取得
|
||||||
|
|
||||||
|
CouchDBのMangoクエリでは文字列の部分一致は
|
||||||
|
範囲指定($gte, $lt)を使う方法が一般的です
|
||||||
|
("startkey" などでは存在しない)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const result = await db.find({
|
||||||
|
selector: {
|
||||||
|
name: {
|
||||||
|
"$gte": "A",
|
||||||
|
"$lt": "B"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sort: [{ name: "asc" }]
|
||||||
|
});
|
||||||
|
result.docs.forEach(doc => console.log(doc));
|
||||||
|
```
|
||||||
|
|
||||||
|
#### OR条件を使いたい($or)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const result = await db.find({
|
||||||
|
selector: {
|
||||||
|
"$or": [
|
||||||
|
{ age: { "$lt": 30 } },
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
"$gte": "Z",
|
||||||
|
"$lt": "Z\ufff0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
複数条件やソートを行う場合は
|
||||||
|
インデックスも複数フィールドに対応しておくと効率が良くなります
|
||||||
|
|
||||||
|
```js
|
||||||
|
await db.createIndex({
|
||||||
|
index: {
|
||||||
|
fields: ['age', 'name']
|
||||||
|
},
|
||||||
|
name: 'age-name-index'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### フロントエンドで使えるのか
|
||||||
|
|
||||||
|
認証情報がURLに含まれるためセキュリティ上もフロントでの使用は不適切
|
||||||
|
|
||||||
|
* フロントエンドからCouchDBを扱いたい場合:
|
||||||
|
1. **PouchDBを活用する**
|
||||||
|
* フロント向けCouchDB互換DB。リアルタイム同期・オフライン対応も可能
|
||||||
|
2. サーバー側でnanoを使いREST APIを作ってフロントから叩く
|
||||||
|
|
48
src/script/couchDB/controlDB.js
Normal file
48
src/script/couchDB/controlDB.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* Create a new database
|
||||||
|
* nanoでデータベースを作成する
|
||||||
|
*/
|
||||||
|
const nano = require('nano')('http://admin:secret@localhost:5984');
|
||||||
|
|
||||||
|
// データベース作成
|
||||||
|
const createDB = async (dbName) => {
|
||||||
|
nano.db.create(dbName, (err, body) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// データベース削除
|
||||||
|
const deleteDB = async (dbName) => {
|
||||||
|
nano.db.destroy(dbName, (err, body) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// データベース一覧表示
|
||||||
|
const getDbList = async () => {
|
||||||
|
nano.db.list((err, body) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// await deleteDB('user');
|
||||||
|
// await createDB('product');
|
||||||
|
// await createDB('item');
|
||||||
|
// await getDbList();
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
85
src/script/couchDB/controlDoc.js
Normal file
85
src/script/couchDB/controlDoc.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* Create a new database
|
||||||
|
* nanoでデータベースを作成する
|
||||||
|
*/
|
||||||
|
const nano = require('nano')('http://admin:secret@localhost:5984');
|
||||||
|
|
||||||
|
// ドキュメント作成
|
||||||
|
const createDocument = async (dbName,post) => {
|
||||||
|
const db = nano.use(dbName);
|
||||||
|
db.insert(post, (err, body) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ドキュメントを一覧を取得する
|
||||||
|
const getDocumentList = async (dbName) => {
|
||||||
|
const db = nano.use(dbName);
|
||||||
|
const result = await db.list({ include_docs: true });
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ドキュメントを一覧を取得する(抽出する)
|
||||||
|
const findDocument = async (dbName,selector) => {
|
||||||
|
const db = nano.use(dbName);
|
||||||
|
const result = await db.find({selector: selector});
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ドキュメントを取得する
|
||||||
|
const getDocument = async (dbName,docId) => {
|
||||||
|
const db = nano.use(dbName);
|
||||||
|
const doc = await db.get(docId);
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ドキュメントを更新する
|
||||||
|
const updateDocument = async (dbName,doc) => {
|
||||||
|
const db = nano.use(dbName);
|
||||||
|
return await db.insert(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ドキュメントを削除する
|
||||||
|
const deleteDocument = async (dbName,doc) => {
|
||||||
|
const db = nano.use(dbName);
|
||||||
|
return await db.destroy(doc._id, doc._rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// ドキュメントを作成する
|
||||||
|
await createDocument('user',{id:1,name:'Alice',email:'alice@example.com'});
|
||||||
|
// IDを指定する
|
||||||
|
await createDocument('user',{_id:'user_1',id:2,name:'Bob',email:'bob@example.com'});
|
||||||
|
|
||||||
|
// ドキュメントを取得する
|
||||||
|
const doc = await getDocument('user','user_1');
|
||||||
|
console.log(doc.name);
|
||||||
|
|
||||||
|
// ドキュメントを更新する
|
||||||
|
doc.age = 20;
|
||||||
|
await updateDocument('user',doc);
|
||||||
|
|
||||||
|
// ドキュメントを削除する
|
||||||
|
await deleteDocument('user',doc);
|
||||||
|
|
||||||
|
// ドキュメントの一覧を取得する
|
||||||
|
const docs = await getDocumentList('user');
|
||||||
|
console.log(docs);
|
||||||
|
|
||||||
|
// ドキュメントの検索する
|
||||||
|
const selector = {name: 'Bob'};
|
||||||
|
docs = await findDocument('user',selector);
|
||||||
|
console.log(docs);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
199
src/script/minio/README.md
Normal file
199
src/script/minio/README.md
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
# MinIO 利用ガイド
|
||||||
|
|
||||||
|
## 概要
|
||||||
|
|
||||||
|
[MinIO](https://min.io/) は Amazon S3 互換のオブジェクトストレージサーバー。軽量かつシンプルな構成で、オンプレミスやクラウド上に S3 互換のストレージ環境を構築できる。
|
||||||
|
|
||||||
|
高可用構成やセキュリティ機能も備えており、小規模から大規模用途まで幅広く利用できる。本記事では MinIO の導入方法、仕様要件、ユースケース、基本的な利用方法についてまとめる。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. インストール方法
|
||||||
|
|
||||||
|
### 1.1 Docker を使う場合
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -p 9000:9000 -p 9001:9001 \
|
||||||
|
--name minio \
|
||||||
|
-e "MINIO_ROOT_USER=admin" \
|
||||||
|
-e "MINIO_ROOT_PASSWORD=password123" \
|
||||||
|
-v /mnt/data:/data \
|
||||||
|
quay.io/minio/minio server /data --console-address ":9001"
|
||||||
|
```
|
||||||
|
|
||||||
|
- `9000`: S3互換API用ポート
|
||||||
|
- `9001`: Webコンソール用ポート
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.2 Docker Compose を使う場合
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
minio:
|
||||||
|
image: quay.io/minio/minio
|
||||||
|
ports:
|
||||||
|
- "9000:9000"
|
||||||
|
- "9001:9001"
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: admin
|
||||||
|
MINIO_ROOT_PASSWORD: password123
|
||||||
|
command: server /data --console-address ":9001"
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
|
起動コマンド:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.3 バイナリから直接実行する場合(Linux)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://dl.min.io/server/minio/release/linux-amd64/minio
|
||||||
|
chmod +x minio
|
||||||
|
sudo mv minio /usr/local/bin/
|
||||||
|
|
||||||
|
export MINIO_ROOT_USER=admin
|
||||||
|
export MINIO_ROOT_PASSWORD=password123
|
||||||
|
minio server /mnt/data --console-address ":9001"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 仕様要件
|
||||||
|
|
||||||
|
| 項目 | 推奨構成例 |
|
||||||
|
|--------------|------------------------------|
|
||||||
|
| OS | Linux(Ubuntu 22.04 など) |
|
||||||
|
| CPU | 1コア以上(最低限) |
|
||||||
|
| メモリ | 1GB 以上(2GB 以上推奨) |
|
||||||
|
| ストレージ | SSD または HDD |
|
||||||
|
| 通信 | ポート 9000 / 9001(開放) |
|
||||||
|
|
||||||
|
- HTTPS を使用する場合は TLS 証明書(Let's Encrypt など)を導入
|
||||||
|
- リバースプロキシ(Nginx/Caddyなど)経由での公開も可能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. ユースケース・機能
|
||||||
|
|
||||||
|
### 主なユースケース
|
||||||
|
|
||||||
|
- バックアップ保存
|
||||||
|
- Webアプリの画像・動画ストレージ
|
||||||
|
- ログデータ保管
|
||||||
|
- オンプレS3互換ストレージ
|
||||||
|
|
||||||
|
### 主な機能
|
||||||
|
|
||||||
|
- S3 互換API対応
|
||||||
|
- Webベース管理コンソール
|
||||||
|
- IAM風のユーザー・ポリシー制御
|
||||||
|
- TLS対応(HTTPS通信)
|
||||||
|
- サーバーサイド暗号化(AES-256)
|
||||||
|
- マルチテナント対応(認証連携含む)
|
||||||
|
- オブジェクトのバージョン管理(設定時)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 実際の使い方
|
||||||
|
|
||||||
|
サンプルコードでは以下を作成しております
|
||||||
|
|
||||||
|
1. バケット作成 : sample-data(readwrite)
|
||||||
|
2. ユーザー設定 : appuser:password123
|
||||||
|
|
||||||
|
|
||||||
|
### Webコンソールを利用する場合
|
||||||
|
|
||||||
|
#### Webコンソールにアクセス
|
||||||
|
|
||||||
|
ブラウザで `http://<ホスト>:9001` にアクセス
|
||||||
|
管理者アカウントでログイン(`MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### バケット作成・ファイルアップロード
|
||||||
|
|
||||||
|
- Web UI からバケット作成
|
||||||
|
- ローカルからファイルをアップロード可能
|
||||||
|
- バケット単位で「パブリックアクセス許可」も可能
|
||||||
|
|
||||||
|
### `mc` コマンドによる操作
|
||||||
|
|
||||||
|
#### インストール
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://dl.min.io/client/mc/release/linux-amd64/mc
|
||||||
|
chmod +x mc
|
||||||
|
sudo mv mc /usr/local/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### MinIOへの接続
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mc alias set local http://localhost:9000 admin password123
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 操作例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mc mb local/mybucket
|
||||||
|
mc cp ./example.jpg local/mybucket/
|
||||||
|
mc ls local/mybucket
|
||||||
|
```
|
||||||
|
|
||||||
|
### 各プログラム言語のライブラリから利用する場合
|
||||||
|
|
||||||
|
MinIOはAmazon S3互換のAPIを提供しているためS3対応の
|
||||||
|
クライアントライブラリを使用すればPythonやNode.jsから
|
||||||
|
簡単に接続・操作できます
|
||||||
|
|
||||||
|
* Python: `boto3`
|
||||||
|
* Node.js: `@aws-sdk/client-s3`
|
||||||
|
|
||||||
|
**接続時の注意点**
|
||||||
|
* リージョン
|
||||||
|
* 仮に指定しますが実際は無視される
|
||||||
|
* バージョニング/ライフサイクル/通知
|
||||||
|
* 一部のS3機能はMinIOでサポートされていない、または挙動が違う場合あり
|
||||||
|
* 署名付きURL
|
||||||
|
* boto3などで生成する署名付きURLも動作しますが、MinIOの設定によって挙動が異なることがある
|
||||||
|
|
||||||
|
### minioライブラリ(MinIO公式)
|
||||||
|
|
||||||
|
MinIO専用に最適化されていて、軽量・シンプルで扱いやすいのが特徴です。
|
||||||
|
S3互換APIを使ってはいますがAmazon純正のSDK(@aws-sdk/*)と違いMinIOのユースケースに特化してます
|
||||||
|
|
||||||
|
#### インストール方法
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install minio
|
||||||
|
```
|
||||||
|
|
||||||
|
#### インストール方法
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 付録:Tips
|
||||||
|
|
||||||
|
- リバースプロキシ(Nginx)を通して HTTPS 公開することで、ブラウザ互換性やセキュリティを向上できる
|
||||||
|
- Cloudflare などのCDNと組み合わせれば、簡易的なグローバル配信も可能
|
||||||
|
- アプリからは AWS SDK を使ってアクセス可能(S3互換)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## まとめ
|
||||||
|
|
||||||
|
MinIO は S3互換かつ軽量で、開発・検証・軽量サービスにおけるストレージニーズに柔軟に対応できる。Docker環境でも素早く構築可能なため、内部ツールやバックアップ用途として導入検討する価値がある。
|
20
src/script/minio/deleteFile.js
Normal file
20
src/script/minio/deleteFile.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const Minio = require('minio');
|
||||||
|
|
||||||
|
const minioClient = new Minio.Client({
|
||||||
|
endPoint: 'localhost',
|
||||||
|
port: 9000,
|
||||||
|
useSSL: false,
|
||||||
|
accessKey: 'appuser',
|
||||||
|
secretKey: 'password123'
|
||||||
|
});
|
||||||
|
|
||||||
|
const BUCKET_NAME = 'sample-data';
|
||||||
|
const OBJECT_NAME = 'sample2.csv';
|
||||||
|
|
||||||
|
minioClient.removeObject(BUCKET_NAME, OBJECT_NAME, (err) => {
|
||||||
|
if (err) {
|
||||||
|
return console.log("file delete error:", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('File deleted successfully.');
|
||||||
|
});
|
19
src/script/minio/deleteFiles.js
Normal file
19
src/script/minio/deleteFiles.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
const Minio = require('minio');
|
||||||
|
|
||||||
|
const minioClient = new Minio.Client({
|
||||||
|
endPoint: 'localhost',
|
||||||
|
port: 9000,
|
||||||
|
useSSL: false,
|
||||||
|
accessKey: 'appuser',
|
||||||
|
secretKey: 'password123'
|
||||||
|
});
|
||||||
|
|
||||||
|
const BUCKET_NAME = 'sample-data';
|
||||||
|
const OBJECT_NAMES = ['file1.csv', 'file2.csv'];
|
||||||
|
minioClient.removeObjects(BUCKET_NAME, OBJECT_NAMES, (err) => {
|
||||||
|
if (err) {
|
||||||
|
return console.log("files delete error:", err);
|
||||||
|
}
|
||||||
|
console.log('Files deleted successfully.');
|
||||||
|
});
|
||||||
|
|
35
src/script/minio/getFile.js
Normal file
35
src/script/minio/getFile.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* MinIOに保存されているファイル一覧を取得する
|
||||||
|
*/
|
||||||
|
const Minio = require('minio');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const minioClient = new Minio.Client({
|
||||||
|
endPoint: 'localhost',
|
||||||
|
port: 9000,
|
||||||
|
useSSL: false,
|
||||||
|
accessKey: 'appuser',
|
||||||
|
secretKey: 'password123'
|
||||||
|
});
|
||||||
|
const BUCKET_NAME = 'sample-data';
|
||||||
|
const OBJECT_NAME = 'sample1.csv';
|
||||||
|
|
||||||
|
minioClient.getObject(BUCKET_NAME, OBJECT_NAME, (err, dataStream) => {
|
||||||
|
if (err) {
|
||||||
|
return console.log("file get error:", err);
|
||||||
|
}
|
||||||
|
const file = fs.createWriteStream('./sample1_direct.csv');
|
||||||
|
dataStream.pipe(file);
|
||||||
|
console.log('File downloaded successfully.');
|
||||||
|
|
||||||
|
dataStream.on('end', () => {
|
||||||
|
console.log('File Download File end');
|
||||||
|
});
|
||||||
|
|
||||||
|
dataStream.on('error', err => {
|
||||||
|
console.error('File Download Err:', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
28
src/script/minio/getFileList.js
Normal file
28
src/script/minio/getFileList.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* MinIOに保存されているファイル一覧を取得する
|
||||||
|
*/
|
||||||
|
const Minio = require('minio');
|
||||||
|
|
||||||
|
const minioClient = new Minio.Client({
|
||||||
|
endPoint: 'localhost',
|
||||||
|
port: 9000,
|
||||||
|
useSSL: false,
|
||||||
|
accessKey: 'appuser',
|
||||||
|
secretKey: 'password123'
|
||||||
|
});
|
||||||
|
const BUCKET_NAME = 'sample-data';
|
||||||
|
|
||||||
|
// listObjects はストリームなので順に読み取る必要があります
|
||||||
|
const stream = minioClient.listObjects(BUCKET_NAME, '', true); // true: 再帰的にすべて取得
|
||||||
|
|
||||||
|
stream.on('data', obj => {
|
||||||
|
console.log(`data ${obj.name} (${obj.size} bytes)`);
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.on('end', () => {
|
||||||
|
console.log('Get File list end');
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.on('error', err => {
|
||||||
|
console.error('Files get Err:', err);
|
||||||
|
});
|
27
src/script/minio/getFileListAsync.js
Normal file
27
src/script/minio/getFileListAsync.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* MinIOに保存されているファイル一覧を非同期関数で取得する
|
||||||
|
*/
|
||||||
|
const Minio = require('minio');
|
||||||
|
|
||||||
|
const minioClient = new Minio.Client({
|
||||||
|
endPoint: 'localhost',
|
||||||
|
port: 9000,
|
||||||
|
useSSL: false,
|
||||||
|
accessKey: 'appuser',
|
||||||
|
secretKey: 'password123'
|
||||||
|
});
|
||||||
|
const BUCKET_NAME = 'sample-data';
|
||||||
|
|
||||||
|
function listAllObjects(bucket) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const result = [];
|
||||||
|
const stream = minioClient.listObjects(bucket, '', true);
|
||||||
|
|
||||||
|
stream.on('data', obj => result.push(obj));
|
||||||
|
stream.on('end', () => resolve(result));
|
||||||
|
stream.on('error', reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const objects = await listAllObjects(BUCKET_NAME);
|
||||||
|
console.log(objects.map(obj => obj.name));
|
32
src/script/minio/getSignatureURL.js
Normal file
32
src/script/minio/getSignatureURL.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
const Minio = require('minio');
|
||||||
|
|
||||||
|
const BUCKET_NAME = 'sample-data';
|
||||||
|
|
||||||
|
const minioClient = new Minio.Client({
|
||||||
|
endPoint: 'localhost',
|
||||||
|
port: 9000,
|
||||||
|
useSSL: false,
|
||||||
|
accessKey: 'appuser',
|
||||||
|
secretKey: 'password123'
|
||||||
|
});
|
||||||
|
|
||||||
|
// presigned GET URL を生成(有効期限: 1時間)
|
||||||
|
minioClient.presignedGetObject(BUCKET_NAME, 'sample2.csv', 60 * 60, (err, url) => {
|
||||||
|
if (err) {
|
||||||
|
return console.log("file get signatureURL error:", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Presigned GET URL:', url)
|
||||||
|
|
||||||
|
// fetchで取得してファイルに保存(任意)
|
||||||
|
// fetch(url)
|
||||||
|
// .then(res => res.buffer())
|
||||||
|
// .then(data => {
|
||||||
|
// // Bufferを使ってファイルに保存(オプション)
|
||||||
|
// import('fs').then(fs => {
|
||||||
|
// fs.writeFileSync('./downloaded_sample2.csv', data);
|
||||||
|
// console.log('File downloaded successfully.');
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
22
src/script/minio/upLoadFile.js
Normal file
22
src/script/minio/upLoadFile.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
const Minio = require('minio');
|
||||||
|
|
||||||
|
const BUCKET_NAME = 'sample-data';
|
||||||
|
|
||||||
|
const minioClient = new Minio.Client({
|
||||||
|
endPoint: 'localhost',
|
||||||
|
port: 9000,
|
||||||
|
useSSL: false,
|
||||||
|
accessKey: 'appuser',
|
||||||
|
secretKey: 'password123'
|
||||||
|
});
|
||||||
|
|
||||||
|
// ファイルアップロード(src/data/sample1.csv)
|
||||||
|
minioClient.fPutObject(BUCKET_NAME, 'sample1.csv', './src/data/sample1.csv', function(err, etag) {
|
||||||
|
if (err){
|
||||||
|
return console.log("file upload error:", err);
|
||||||
|
}
|
||||||
|
console.log('File uploaded successfully.',etag);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
30
src/script/minio/upLoadSignatureURL.js
Normal file
30
src/script/minio/upLoadSignatureURL.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
const Minio = require('minio');
|
||||||
|
|
||||||
|
const BUCKET_NAME = 'sample-data';
|
||||||
|
|
||||||
|
const minioClient = new Minio.Client({
|
||||||
|
endPoint: 'localhost',
|
||||||
|
port: 9000,
|
||||||
|
useSSL: false,
|
||||||
|
accessKey: 'appuser',
|
||||||
|
secretKey: 'password123'
|
||||||
|
});
|
||||||
|
|
||||||
|
// ファイルアップロード(src/data/sample1.csv)
|
||||||
|
minioClient.presignedPutObject(BUCKET_NAME, 'sample2.csv',60 * 60, function(err, presignedUrl) {
|
||||||
|
if (err){
|
||||||
|
return console.log("file upload error:", err);
|
||||||
|
}
|
||||||
|
console.log('File uploaded URL:',presignedUrl);
|
||||||
|
// ダウンロードしたURLにPUTリクエストを送信する
|
||||||
|
});
|
||||||
|
|
||||||
|
// curl -X PUT -T .\src\data\sample2.csv "presignedUrl"
|
||||||
|
// const fileData = await fs.readFile(filePath);
|
||||||
|
// const res = await fetch(uploadUrl, {
|
||||||
|
// method: 'PUT',
|
||||||
|
// body: fileData,
|
||||||
|
// headers: {
|
||||||
|
// 'Content-Type': 'text/csv'
|
||||||
|
// }
|
||||||
|
// });
|
53
src/script/uploadFileFromSignadUrl.js
Normal file
53
src/script/uploadFileFromSignadUrl.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
|
async function uploadFile(filePath) {
|
||||||
|
try {
|
||||||
|
// Step 1: Request a signed URL from the API
|
||||||
|
const apiResponse = await fetch('http://localhost:3000/api/get-signed-url', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
fileName: path.basename(filePath),
|
||||||
|
contentType: 'application/octet-stream',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!apiResponse.ok) {
|
||||||
|
throw new Error(`Failed to retrieve signed URL: ${apiResponse.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { signedUrl } = await apiResponse.json();
|
||||||
|
|
||||||
|
if (!signedUrl) {
|
||||||
|
throw new Error('Signed URL is missing in the response');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Signed URL received:', signedUrl);
|
||||||
|
|
||||||
|
// Step 2: Upload the file to the signed URL using PUT
|
||||||
|
const fileStream = fs.createReadStream(filePath);
|
||||||
|
const uploadResponse = await fetch(signedUrl, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/octet-stream',
|
||||||
|
},
|
||||||
|
body: fileStream,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!uploadResponse.ok) {
|
||||||
|
throw new Error(`Failed to upload file: ${uploadResponse.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('File uploaded successfully:', uploadResponse.status);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error uploading file:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage
|
||||||
|
const filePath = './example-file.bin'; // Replace with your file path
|
||||||
|
uploadFile(filePath);
|
@ -32,6 +32,7 @@ const downloadFilesFromGCS = async (req, res) => {
|
|||||||
const archive = archiver('zip', { zlib: { level: 9 } });
|
const archive = archiver('zip', { zlib: { level: 9 } });
|
||||||
|
|
||||||
archive.on('error', (err) => res.status(500).send({ error: err.message }));
|
archive.on('error', (err) => res.status(500).send({ error: err.message }));
|
||||||
|
// レスポンスにアーカイブをパイプする
|
||||||
archive.pipe(res);
|
archive.pipe(res);
|
||||||
|
|
||||||
|
|
||||||
|
65
src/server/download/gcsArchiveJson.js
Normal file
65
src/server/download/gcsArchiveJson.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Google Cloud Storage (GCS) download module.
|
||||||
|
*/
|
||||||
|
const { Storage } = require('@google-cloud/storage');
|
||||||
|
const { PassThrough } = require('stream');
|
||||||
|
const { finished } = require('stream/promises');
|
||||||
|
|
||||||
|
const archiver = require('archiver');
|
||||||
|
const KEY_FILE_PATH = './keys/service-account.json'
|
||||||
|
const storage = new Storage({
|
||||||
|
keyFilename: KEY_FILE_PATH});
|
||||||
|
|
||||||
|
// Load environment variables
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
// バケット名を.envから取得する
|
||||||
|
const BUCKET_NAME = process.env.BUCKET_NAME;
|
||||||
|
console.log(`BUCKET_NAME: ${BUCKET_NAME}`);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GCStorageからファイルをダウンロードする
|
||||||
|
*
|
||||||
|
* @param {http.IncomingMessage} req
|
||||||
|
* @param {http.ServerResponse} res
|
||||||
|
*/
|
||||||
|
const downloadFilesFromGCSJson = async (req, res) => {
|
||||||
|
// バケットからファイル一覧を取得する
|
||||||
|
const [files] = await storage.bucket(BUCKET_NAME).getFiles();
|
||||||
|
const filesToZip = files.map((file) => file.name);
|
||||||
|
|
||||||
|
// レスポンスはJSONでBase64のZIP情報を返す
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', '*'); // すべてのオリジンを許可
|
||||||
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
||||||
|
res.setHeader('Content-Type', 'application/json');
|
||||||
|
|
||||||
|
// Streamを作成する
|
||||||
|
const archive = archiver('zip', { zlib: { level: 9 } });
|
||||||
|
const pass = new PassThrough();
|
||||||
|
const chunks = [];
|
||||||
|
|
||||||
|
// チャンク収集
|
||||||
|
pass.on('data', chunk => chunks.push(chunk));
|
||||||
|
|
||||||
|
for (const fileName of filesToZip) {
|
||||||
|
const file = storage.bucket(BUCKET_NAME).file(fileName);
|
||||||
|
archive.append(file.createReadStream(), { name: fileName });
|
||||||
|
}
|
||||||
|
|
||||||
|
archive.pipe(pass);
|
||||||
|
archive.on('error', (err) => res.status(500).send({ error: err.message }));
|
||||||
|
|
||||||
|
//
|
||||||
|
await archive.finalize();
|
||||||
|
|
||||||
|
// アーカイブ(zipなど)の入力の完了を宣言して書き込みを開始させる命令です。
|
||||||
|
await finished(pass);
|
||||||
|
|
||||||
|
// Base64エンコードしてJSONで返す
|
||||||
|
const base64 = Buffer.concat(chunks).toString('base64');
|
||||||
|
res.end(JSON.stringify({ data: base64 }));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = downloadFilesFromGCSJson;
|
@ -2,6 +2,7 @@ const http = require('http');
|
|||||||
|
|
||||||
const { downloadContents, generateZipUrl } = require('./download/download');
|
const { downloadContents, generateZipUrl } = require('./download/download');
|
||||||
const downloadFilesFromGCS = require('./download/gcsArchive');
|
const downloadFilesFromGCS = require('./download/gcsArchive');
|
||||||
|
const downloadFilesFromGCSJson = require('./download/gcsArchiveJson');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -24,6 +25,9 @@ const server = http.createServer((req, res) => {
|
|||||||
} else if (req.url === '/downdload-gcs') {
|
} else if (req.url === '/downdload-gcs') {
|
||||||
// GCSからZIPファイルのコンテンツをそのまま返す
|
// GCSからZIPファイルのコンテンツをそのまま返す
|
||||||
downloadFilesFromGCS(req, res);
|
downloadFilesFromGCS(req, res);
|
||||||
|
} else if (req.url === '/downdload-gcs-json') {
|
||||||
|
// GCSからZIPファイルのコンテンツをそのまま返す
|
||||||
|
downloadFilesFromGCSJson(req, res);
|
||||||
} else {
|
} else {
|
||||||
res.statusCode = 404;
|
res.statusCode = 404;
|
||||||
res.setHeader('Content-Type', 'text/plain');
|
res.setHeader('Content-Type', 'text/plain');
|
||||||
|
37
src/types/INoSQL.ts
Normal file
37
src/types/INoSQL.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Interface for NoSQL document
|
||||||
|
* @param T - Type of data
|
||||||
|
* @param id - Document ID
|
||||||
|
*/
|
||||||
|
export interface IDocument<T> {
|
||||||
|
id: string;
|
||||||
|
data: T;
|
||||||
|
createdAt?: Date;
|
||||||
|
updatedAt?: Date;
|
||||||
|
version?: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to unsubscribe from updates *
|
||||||
|
* watch()を使わない・購読解除の必要もないならvoid戻り値でも問題なし
|
||||||
|
* 将来的には、unsubscribeするための関数を返す
|
||||||
|
*/
|
||||||
|
export type UnsubscribeFn = () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for NoSQL collection
|
||||||
|
* @param T - Type of data
|
||||||
|
*/
|
||||||
|
export interface ICollection<T> {
|
||||||
|
get(id: string): Promise<IDocument<T> | null>;
|
||||||
|
list(): Promise<IDocument<T>[]>;
|
||||||
|
create(data: T): Promise<IDocument<T>>;
|
||||||
|
update(id: string, data: Partial<T>): Promise<IDocument<T>>;
|
||||||
|
delete(id: string): Promise<void>;
|
||||||
|
// オプション: 更新監視など
|
||||||
|
watch?(onChange: (doc: IDocument<T>) => void): UnsubscribeFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDatabaseClient {
|
||||||
|
getCollection<T>(name: string): ICollection<T>;
|
||||||
|
}
|
75
src/types/IStorage.d.ts
vendored
Normal file
75
src/types/IStorage.d.ts
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* 汎用的なクラウドストレージインターフェース(S3 / GCS / MinIO など対応)
|
||||||
|
*/
|
||||||
|
export enum ProcessType {
|
||||||
|
GET = 0,
|
||||||
|
PUT = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IStorage {
|
||||||
|
/**
|
||||||
|
* ローカルファイルをアップロードする
|
||||||
|
* @param filePath ローカルファイルのパス
|
||||||
|
* @param objectKey ストレージ上でのオブジェクトキー(パス)
|
||||||
|
*/
|
||||||
|
uploadFile: (filePath: string, objectKey: string) => Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* メモリ上のデータをアップロードする(バッファまたは文字列)
|
||||||
|
* @param fileData アップロード対象のデータ
|
||||||
|
* @param objectKey オブジェクトキー
|
||||||
|
* @param contentType Content-Type(省略時は application/octet-stream)
|
||||||
|
*/
|
||||||
|
uploadContents?: (
|
||||||
|
fileData: Buffer | string,
|
||||||
|
objectKey: string,
|
||||||
|
contentType?: string
|
||||||
|
) => Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ファイルをローカルにダウンロードする
|
||||||
|
* @param objectKey 取得するオブジェクトキー
|
||||||
|
* @param destinationPath 保存先ローカルファイルパス
|
||||||
|
* @returns ダウンロードしたファイルの内容(Buffer)
|
||||||
|
*/
|
||||||
|
downloadFile(objectKey: string, destinationPath: string): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オブジェクトをメモリ上に直接取得する(ファイル保存しない)
|
||||||
|
* @param objectKey 取得するオブジェクトキー
|
||||||
|
* @returns オブジェクトの内容(Buffer)
|
||||||
|
*/
|
||||||
|
downloadContents?(objectKey: string): Promise<Buffer>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 単一ファイル(オブジェクト)を削除する
|
||||||
|
* @param objectKey 削除するオブジェクトキー
|
||||||
|
*/
|
||||||
|
delete(objectKey: string): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 複数のファイルをまとめて削除する(サポートされている場合)
|
||||||
|
* @param objectKeys 削除対象のオブジェクトキー配列
|
||||||
|
*/
|
||||||
|
deleteMany?(objectKeys: string[]): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* バケット内のファイル一覧を取得する
|
||||||
|
* @param prefix 指定したプレフィックスでフィルタリング(省略可)
|
||||||
|
* @returns 一致するオブジェクトキーの一覧
|
||||||
|
*/
|
||||||
|
getFileList(prefix?: string): Promise<string[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 署名付きURLを発行する
|
||||||
|
* @param objectKey 対象のオブジェクトキー
|
||||||
|
* @param processType 処理タイプ(例: 0 = GET, 1 = PUT など)
|
||||||
|
* @param options 有効期限などのオプション
|
||||||
|
* @returns 署名付きURL
|
||||||
|
*/
|
||||||
|
getSignedUrl(
|
||||||
|
objectKey: string,
|
||||||
|
processType: ProcessType,
|
||||||
|
options: { expiresIn: number }
|
||||||
|
): Promise<string>;
|
||||||
|
}
|
0
tests/test_common.js
Normal file
0
tests/test_common.js
Normal file
Loading…
x
Reference in New Issue
Block a user