<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:gd="http://schemas.google.com/g/2005" xmlns:georss="http://www.georss.org/georss" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-4654305895942962500</atom:id><lastBuildDate>Tue, 17 Mar 2026 07:55:25 +0000</lastBuildDate><category>實作筆記</category><category>冗長碎碎唸</category><category>iOS</category><category>電腦資訊</category><category>Android</category><category>php</category><category>aws</category><category>ubuntu</category><category>python</category><category>linux</category><category>mysql</category><category>js</category><category>app</category><category>nginx</category><category>api</category><category>javascript</category><category>macos</category><category>google</category><category>docker</category><category>server</category><category>影劇音樂</category><category>電玩卡漫</category><category>MAC</category><category>facebook</category><category>https</category><category>codeigniter</category><category>xcode</category><category>OSX</category><category>mongodb</category><category>sdk</category><category>AndroidBoard</category><category>iPhone</category><category>node.js</category><category>git</category><category>ssl</category><category>足跡回憶</category><category>C</category><category>ELB</category><category>GA</category><category>ansible</category><category>ec2</category><category>go</category><category>http</category><category>ipad</category><category>test</category><category>web</category><category>apache</category><category>apple</category><category>azure</category><category>backup</category><category>build</category><category>ci</category><category>client</category><category>file</category><category>filter</category><category>golang</category><category>image</category><category>install</category><category>json</category><category>login</category><category>openssl</category><category>sql</category><category>windows</category><category>旅遊嘗鮮</category><category>Android RenderScript</category><category>c++</category><category>chromecast</category><category>create</category><category>deploy</category><category>dns</category><category>node</category><category>notification</category><category>RDS</category><category>Synology</category><category>adsense</category><category>amazon</category><category>check</category><category>curl</category><category>geoip</category><category>linode</category><category>nas</category><category>setup</category><category>ssh</category><category>update</category><category>vpn</category><category>小米</category><category>Electron</category><category>admob</category><category>ai</category><category>auth</category><category>command</category><category>desktop</category><category>email</category><category>find</category><category>join</category><category>maxmind</category><category>netflix</category><category>oauth</category><category>objc</category><category>proxy</category><category>push</category><category>review</category><category>select</category><category>user</category><category>wget</category><category>12.04</category><category>USB</category><category>android studio</category><category>auto</category><category>change</category><category>cmake</category><category>csv</category><category>download</category><category>embedded linux</category><category>event</category><category>export</category><category>font</category><category>free</category><category>gitlab</category><category>id</category><category>index</category><category>java</category><category>jenkins</category><category>jq</category><category>list</category><category>mantis</category><category>memory</category><category>monitor</category><category>objective c</category><category>pass</category><category>password</category><category>phpmyadmin</category><category>python3</category><category>tls</category><category>upgrade</category><category>validator</category><category>生活法律</category><category>韓劇</category><category>4k</category><category>IAM</category><category>LLVM</category><category>SQLite</category><category>SSD</category><category>account</category><category>bash</category><category>cli</category><category>cloud</category><category>compile</category><category>container</category><category>cpp</category><category>db</category><category>disable</category><category>error</category><category>ffmpeg</category><category>flutter</category><category>g++</category><category>graph</category><category>grep</category><category>html</category><category>jni</category><category>key</category><category>library</category><category>line</category><category>location</category><category>macbook</category><category>name</category><category>ndk</category><category>nodejs</category><category>objctive c</category><category>openssh</category><category>pCloud</category><category>playbook</category><category>pymongo</category><category>remote</category><category>replace</category><category>request</category><category>route</category><category>s3</category><category>script</category><category>service</category><category>sklearn</category><category>soap</category><category>storyboard</category><category>string</category><category>sync</category><category>table</category><category>unit</category><category>upload</category><category>url</category><category>vim</category><category>windows11</category><category>台灣</category><category>筆記</category><category>GAE</category><category>Google Sheets</category><category>Gradle</category><category>OpenClaw</category><category>PaaS</category><category>Puppeteer</category><category>alias</category><category>apns</category><category>book</category><category>browser</category><category>case</category><category>centos</category><category>cgi</category><category>cicd</category><category>cloudflare</category><category>cocoapods</category><category>comm</category><category>convert</category><category>crawler</category><category>crontab</category><category>cryptomator</category><category>custom</category><category>data</category><category>date</category><category>debug</category><category>diff</category><category>domain</category><category>dynamic</category><category>ecs</category><category>fb</category><category>firebase</category><category>format</category><category>framework</category><category>gcm</category><category>geolocation</category><category>glacier</category><category>godaddy</category><category>group</category><category>gtest</category><category>handle</category><category>hashmap</category><category>hostname</category><category>hosts</category><category>import</category><category>ip</category><category>ipsec</category><category>iptables</category><category>itunesconnect</category><category>jquery</category><category>k8s</category><category>kubernetes</category><category>l2tp</category><category>laravel</category><category>limit</category><category>link</category><category>macports</category><category>mail</category><category>map</category><category>md5</category><category>merge</category><category>mi</category><category>mongo</category><category>mongod</category><category>mp4</category><category>multiple</category><category>mysqldump</category><category>not found</category><category>osticket</category><category>pandas</category><category>pcre</category><category>pi</category><category>plugin</category><category>regular express</category><category>remove</category><category>resize</category><category>restore</category><category>rewrite</category><category>routing</category><category>rpm</category><category>sed</category><category>simulator</category><category>sqlite3</category><category>storage</category><category>subtitle</category><category>system</category><category>taiwan</category><category>tar</category><category>terminal</category><category>timezone</category><category>tmux</category><category>token</category><category>tracking</category><category>tw</category><category>uitableviewcontroller</category><category>utf8</category><category>verify</category><category>vite</category><category>vpc</category><category>vue.js</category><category>web server</category><category>webview</category><category>yt-dlp</category><category>小裝置</category><category>戲劇</category><category>教學</category><category>更換</category><category>開箱</category><category>/etc/rc.local</category><category>14.04</category><category>2024</category><category>22.04</category><category>24.04</category><category>4.0</category><category>53</category><category>AMD</category><category>Android One</category><category>AngularJS</category><category>Bootstrap</category><category>ElectronJS</category><category>FCM</category><category>HA</category><category>HEX</category><category>ICAP</category><category>IDFA</category><category>Jinja2</category><category>Measurement Protocol</category><category>NOT</category><category>Pro</category><category>REGEXP</category><category>Raspberry</category><category>Renew</category><category>Replica Set</category><category>SPF</category><category>UVC</category><category>Uncategoried</category><category>VLC</category><category>VM</category><category>Xbox</category><category>Xiaomi</category><category>ads</category><category>aes-128</category><category>alexa</category><category>ami</category><category>analysis</category><category>ansible-playbook</category><category>apn</category><category>apt-get</category><category>arm64</category><category>auth_basic</category><category>auto scaling</category><category>autorun</category><category>autossh</category><category>awk</category><category>backend</category><category>beta</category><category>calculate</category><category>call</category><category>callback</category><category>cast</category><category>charset</category><category>chrome</category><category>clang</category><category>click</category><category>cluster</category><category>cmd</category><category>code</category><category>composer</category><category>content</category><category>count</category><category>country</category><category>covert</category><category>cpu</category><category>crack</category><category>csr</category><category>cygwin</category><category>dashboard</category><category>database</category><category>delete</category><category>des3</category><category>detect</category><category>develop</category><category>development</category><category>device</category><category>devops</category><category>dlink</category><category>docker-compose</category><category>dockerfile</category><category>drive</category><category>driver</category><category>dropbox</category><category>ds216play</category><category>ds723+</category><category>echo</category><category>encoding</category><category>engine</category><category>enterprise</category><category>exec</category><category>exists</category><category>ezcast</category><category>fix</category><category>form</category><category>forward</category><category>function</category><category>fuse</category><category>ga4</category><category>gemini-pro-vision</category><category>generator</category><category>google analytics</category><category>google docs</category><category>google tv</category><category>googletest</category><category>hashcat</category><category>header</category><category>host</category><category>img</category><category>include</category><category>insert</category><category>ios9</category><category>iot</category><category>itunes</category><category>j2</category><category>jwt</category><category>kaggle</category><category>keyfile</category><category>keyword</category><category>kobo</category><category>launch</category><category>ldap</category><category>learning</category><category>let's encrypt</category><category>libcurl</category><category>localhost</category><category>log</category><category>lookup</category><category>m1</category><category>m3u</category><category>m4</category><category>machine</category><category>macro</category><category>macstrap</category><category>mailfetch</category><category>matrix</category><category>md5sum</category><category>mime</category><category>mimedecode</category><category>minikube</category><category>module</category><category>move</category><category>mvc</category><category>mx</category><category>nagios</category><category>network</category><category>note</category><category>npm</category><category>null</category><category>oauth2</category><category>object</category><category>openssh server</category><category>pageview</category><category>parser</category><category>pc</category><category>perl</category><category>php-cs-fixer</category><category>php7</category><category>php8</category><category>ping</category><category>pipe</category><category>pod</category><category>port forwarding</category><category>post</category><category>postfix</category><category>precision</category><category>presentViewController</category><category>pricing</category><category>proftp</category><category>proftpd</category><category>projector</category><category>proxy_pass</category><category>query</category><category>rebuild</category><category>recall</category><category>reduce</category><category>region</category><category>replica</category><category>replication</category><category>repo</category><category>report</category><category>reset</category><category>resource</category><category>retry</category><category>root</category><category>rpmbuild</category><category>rss</category><category>screen</category><category>screenshot</category><category>security</category><category>ses</category><category>set</category><category>share</category><category>skip</category><category>slack</category><category>slave</category><category>smtp</category><category>sort</category><category>spam</category><category>speed</category><category>spotify</category><category>spreadsheet</category><category>startup</category><category>study</category><category>submodule</category><category>subnet</category><category>swift</category><category>switch</category><category>tail</category><category>taipei</category><category>template</category><category>text</category><category>time</category><category>timestamp</category><category>tool</category><category>toto</category><category>transfer</category><category>try_files</category><category>ts</category><category>tunnel</category><category>tv</category><category>uicollectionview</category><category>uitableviewcell</category><category>ultra</category><category>uninstall</category><category>usage</category><category>uuid</category><category>virtual</category><category>vue</category><category>wasm</category><category>webdav</category><category>webpack</category><category>webshot</category><category>websocket</category><category>width</category><category>wifi</category><category>wildcard</category><category>windows10</category><category>xargs</category><category>xib</category><category>xml</category><category>youtube</category><category>yum</category><category>台北101</category><category>宮崎駿</category><category>小米2S</category><category>愛奇藝</category><category>書</category><category>正版</category><category>比較</category><category>漫畫</category><category>照片</category><category>紅米</category><category>網購</category><category>開發</category><category>開發筆記</category><category>電池</category><category>電視棒</category><category>鬼滅之刃</category><category>"MacBookPro12</category><category>$group</category><category>$match</category><category>$ne</category><category>$unwind</category><category>%u2028</category><category>&amp;</category><category>.davfs.tmp</category><category>0 to 1</category><category>0x207</category><category>1"</category><category>1.1.1.1</category><category>1.1.1.2</category><category>1.1.1.3</category><category>10.11</category><category>10.13</category><category>10.2</category><category>106</category><category>1080p</category><category>13</category><category>15</category><category>17again</category><category>18.04</category><category>18again</category><category>1TB</category><category>2.2</category><category>2.4</category><category>2.4.7</category><category>20.0.0</category><category>2012</category><category>2015</category><category>2017</category><category>2019</category><category>2020</category><category>2022</category><category>2028</category><category>205話</category><category>23</category><category>25MB/s</category><category>26集</category><category>3.0.5</category><category>3.11</category><category>307</category><category>32G</category><category>4</category><category>404</category><category>4VHF:Q9JM:I1GP</category><category>4g</category><category>5.1</category><category>5.10</category><category>5.10.0</category><category>5.3</category><category>500</category><category>500GB</category><category>500TW</category><category>512gb</category><category>6.10.0</category><category>60P</category><category>60fps</category><category>6601</category><category>660p</category><category>8.8.4.4</category><category>8.8.8.8</category><category>8B</category><category>8maple</category><category>9.4</category><category>A-Series</category><category>A/B Test</category><category>A1502</category><category>A1582</category><category>A3</category><category>ADONIT NOTE PlUS</category><category>AI Kit</category><category>AIY</category><category>ALAssetsLibrary</category><category>ALiYun</category><category>APC</category><category>ASUS</category><category>ATV</category><category>AVTransport</category><category>Access-Control-Allow-Origin</category><category>AccessToken</category><category>Amazon JP</category><category>AndroidFileTransfer.dmg</category><category>Application loader</category><category>Assets</category><category>AsyncSocket</category><category>BU110</category><category>Beanstalk</category><category>Brython</category><category>Buds</category><category>BuyAndShip</category><category>CDT</category><category>CEF</category><category>CMO</category><category>CMakeLists.txt</category><category>CVE-2024-6387</category><category>Certificate</category><category>ChatGoogleGenerativeAI</category><category>Chromium</category><category>CocoaHeads</category><category>CodeCommit</category><category>Collapse</category><category>Condition</category><category>Content-Disposition</category><category>Copy XPath</category><category>Cosine similarity</category><category>DIR-632</category><category>DIR632</category><category>DRM</category><category>DSM</category><category>DST</category><category>DaVinci</category><category>Debian 12</category><category>DevTools</category><category>Dongle</category><category>E14</category><category>E15</category><category>E27</category><category>EDT</category><category>ERR_UNSAFE_PORT</category><category>ES</category><category>EU</category><category>EZCast Beam J2</category><category>EZCast Beam V3</category><category>Ec2Client</category><category>ElastiCache</category><category>Elastic</category><category>ElasticLoadBalancingClient</category><category>Email::Send</category><category>Encrypt</category><category>FBSDKLoginManager</category><category>FOREIGN</category><category>FacebookResearch</category><category>Finfo</category><category>GADBannerView</category><category>GAICONF</category><category>GAME</category><category>GB</category><category>GCDAsyncSocket</category><category>GENIE</category><category>GOOGLEFINANCE</category><category>GROUP_CONCAT</category><category>Gemini</category><category>Genius Bar</category><category>GnuPG</category><category>Google Home</category><category>Google Home Mini</category><category>Google Script</category><category>Google Voice Kit</category><category>Google合同</category><category>Gradle upgrade</category><category>Grafana</category><category>Gunicorn</category><category>GzipFile</category><category>HDD</category><category>HDR</category><category>HMR</category><category>HTTP_HOST</category><category>IAP</category><category>IMPORTXML</category><category>INNODB</category><category>ISO</category><category>ISO 3166</category><category>ISO 639</category><category>Kindle</category><category>L2</category><category>L3</category><category>LR350</category><category>LSApplicationQueriesSchemes</category><category>LXC</category><category>LabelEncoder</category><category>Lenovo</category><category>MY83</category><category>Mavericks</category><category>Memcached</category><category>MenuItemCompat</category><category>Mini Tower kit</category><category>Modern php</category><category>MyGM</category><category>MyISAM</category><category>N150UA</category><category>NBA 2K20</category><category>NSAllowsArbitraryLoads</category><category>NSAppTransportSecurity</category><category>NSData</category><category>NSInvalidArgumentException</category><category>NSNotification</category><category>NSParagraphStyle</category><category>NSPlaceholderArray</category><category>NSTextAlignmentCenter</category><category>NSTimezone</category><category>NSURLConnection</category><category>NSwitch</category><category>NetTV</category><category>No Rules Rules</category><category>NoCatSplash</category><category>OCR</category><category>OST</category><category>OTT</category><category>OWC</category><category>OneHotEncoder</category><category>Operation not permitted</category><category>PCHOME</category><category>PCIe</category><category>PCIe 3.0</category><category>PCIe 4.0</category><category>PDT</category><category>PSTI</category><category>PTT</category><category>PaLM</category><category>Paperwhite</category><category>Partial</category><category>Pi 3</category><category>Pi5</category><category>Pixel</category><category>Play</category><category>Pressure</category><category>Preview</category><category>Project management</category><category>Pyodide</category><category>R1600</category><category>RDP</category><category>RED-DA</category><category>RFC 3507</category><category>RMA</category><category>Reinstall</category><category>Require all granted</category><category>RetroPie</category><category>Ryzen</category><category>SAM</category><category>SDDDC3-512G</category><category>SNES</category><category>SNS</category><category>SSL3_WRITE_PENDING</category><category>Search: WWW</category><category>Segmentation fault</category><category>Selenium</category><category>SetAVTransportURI</category><category>Sign In with Apple</category><category>Stable Diffusion</category><category>Start-up</category><category>Stick</category><category>TCF</category><category>TCM1485-3R#M</category><category>TW500</category><category>TXT</category><category>TZ</category><category>TZDATA</category><category>TZDB</category><category>Tesseract</category><category>TestFlight</category><category>Thinkpad</category><category>U+2028</category><category>UDN</category><category>UIActivityViewController</category><category>UIApplication</category><category>UICollectionViewCell</category><category>UIDocumentInteractionController</category><category>UIGestureRecognizer</category><category>UIRefreshControl</category><category>UISwipeGestureRecognizer</category><category>UITapGestureRecognizer</category><category>UIVIew</category><category>UPS</category><category>URL redirect</category><category>URLRequest</category><category>URLSession</category><category>USB3</category><category>USB3.0</category><category>USB3.1</category><category>USB3.2</category><category>USB3.2 Gen1</category><category>USB3.2 Gen2</category><category>USB3.2 Gen2x2</category><category>USB4</category><category>USDC</category><category>UTF-8</category><category>Unused</category><category>Utlra HD</category><category>V-TEX</category><category>VNC</category><category>Variable</category><category>Vertex AI</category><category>Video Capture Card</category><category>ViewPagerIndicator</category><category>WKWebView</category><category>Warning</category><category>WebAssembly</category><category>Windows app</category><category>XGBClassifier</category><category>XMLHttpRequest</category><category>XPath</category><category>Xbox One</category><category>Xbox One S</category><category>_viewControllerForSupportedInterfaceOrientationsWithDismissCheck</category><category>aapt</category><category>ab</category><category>about time</category><category>abtest</category><category>acme.sh</category><category>add</category><category>address</category><category>adname</category><category>adt</category><category>adwords</category><category>aes</category><category>aes-256</category><category>age</category><category>agent</category><category>aggregate</category><category>airbnb</category><category>ajax</category><category>alignment</category><category>all</category><category>allow</category><category>amazon rds</category><category>amzn-ami-vpc-nat</category><category>amzon</category><category>analytics</category><category>anchor</category><category>android platform</category><category>android transfer</category><category>ansible_ssh_port</category><category>antivir</category><category>apk</category><category>app id</category><category>app review</category><category>app store</category><category>append</category><category>apt</category><category>array</category><category>assistant</category><category>async</category><category>auc</category><category>audio</category><category>authenticate</category><category>authentication</category><category>authorization</category><category>authz_core</category><category>auto-increment-increment</category><category>auto-increment-offset</category><category>autossh.exe</category><category>available</category><category>average_precision_score</category><category>avi</category><category>avira</category><category>awscli</category><category>awsome</category><category>azureml</category><category>ban</category><category>bank</category><category>base_url</category><category>batch</category><category>bcc</category><category>beam</category><category>beijing</category><category>big5</category><category>billing</category><category>bind</category><category>bing</category><category>binutils</category><category>bitbucket</category><category>block</category><category>blocking</category><category>bloom</category><category>boilerplate</category><category>bookwalker</category><category>boot</category><category>boringssl_context_alert_callback_handler</category><category>boringssl_session_errorlog</category><category>boringssl_session_handshake_error_print</category><category>bot</category><category>bottle</category><category>bottom</category><category>branch</category><category>broadcast</category><category>broken</category><category>bson</category><category>bug</category><category>bugzilla</category><category>built-in</category><category>bulk</category><category>bundle</category><category>button</category><category>c++11</category><category>c-icap</category><category>c-icap-module</category><category>c2dm</category><category>cJSON</category><category>cache</category><category>canOpenURL</category><category>canonical_ids</category><category>captive</category><category>cartodb</category><category>cdn</category><category>certbot</category><category>cgi-bin</category><category>cgic</category><category>chains</category><category>changyy.app</category><category>characterset</category><category>chatbot</category><category>chatgpt</category><category>check_mysql_health</category><category>checker</category><category>checkout</category><category>checksum</category><category>chromecacst</category><category>chromecast 2013</category><category>chromecast 2015</category><category>chromecast第一代</category><category>chromecast第三代</category><category>chromecast第二代</category><category>circles.life</category><category>city</category><category>cityname</category><category>clamav</category><category>clear</category><category>client id</category><category>cloud sync</category><category>cloudfront</category><category>clubhouse</category><category>cms</category><category>code review</category><category>codeDetection</category><category>coding</category><category>column</category><category>com.android.model.application</category><category>combo command</category><category>commit</category><category>concat</category><category>concatenate</category><category>conf</category><category>configure</category><category>connect</category><category>connection</category><category>connectionlimit</category><category>console</category><category>console.log</category><category>containerd</category><category>content-type</category><category>contribution</category><category>control</category><category>controller</category><category>controlpass</category><category>controluser</category><category>cookie</category><category>copilot</category><category>copy</category><category>core</category><category>core dumped</category><category>core text</category><category>country code</category><category>countryname</category><category>cppunit</category><category>crash</category><category>create image</category><category>createdb</category><category>createuser</category><category>credentials</category><category>cron</category><category>cross</category><category>crt</category><category>crypto.com</category><category>crypytomator</category><category>css</category><category>ctime</category><category>custom domain</category><category>custom function</category><category>custom_fields</category><category>customerreview</category><category>cve</category><category>d-link</category><category>dart</category><category>dash</category><category>dat</category><category>data center</category><category>datadir</category><category>datastore</category><category>datetime</category><category>dau</category><category>dav</category><category>day</category><category>dd</category><category>dd wrt</category><category>dd-wrt</category><category>dec</category><category>decode</category><category>decoder</category><category>degit</category><category>delay</category><category>demo</category><category>dencryption</category><category>deny</category><category>dependencies</category><category>describe-instances</category><category>describe-load-balancers</category><category>dev</category><category>devServer</category><category>dev_appserver.py</category><category>dhcp</category><category>didReceiveResponse</category><category>dimension</category><category>dir 632</category><category>direction</category><category>disk</category><category>diskutil</category><category>disney</category><category>dlna</category><category>dns lookup</category><category>do-release-upgrade</category><category>docker registry</category><category>dockerhub</category><category>dom</category><category>donwtime</category><category>dpkg</category><category>drama</category><category>draw</category><category>drawInRect</category><category>drop</category><category>dropboxd</category><category>ds215j</category><category>dsm 7</category><category>du</category><category>duktape</category><category>duplicate</category><category>each</category><category>eb</category><category>ebooklib</category><category>ebooklib.changyy</category><category>eclipse</category><category>ecsTaskExecutionRole</category><category>edit</category><category>editor</category><category>education</category><category>electron stable release</category><category>electron-forge</category><category>electron-vite</category><category>else</category><category>embed</category><category>embedded</category><category>embedding</category><category>enable</category><category>enableExecuteCommand</category><category>enc</category><category>encode</category><category>encoder</category><category>encryption</category><category>endpoint</category><category>engineering</category><category>enter</category><category>epub-image-helper</category><category>equalto</category><category>eval</category><category>excel</category><category>exchange</category><category>exmail</category><category>extension</category><category>extract</category><category>extractor</category><category>fab</category><category>fabric</category><category>facebook.py</category><category>fail2ban</category><category>faiss</category><category>family</category><category>fatal</category><category>feature</category><category>field</category><category>fieldname</category><category>file transfer</category><category>fileMode</category><category>fileTransfer</category><category>filename</category><category>filesystem</category><category>find-package</category><category>firefox</category><category>firmware</category><category>flask</category><category>flow</category><category>foreach</category><category>frame</category><category>free upgrade</category><category>from</category><category>frontend</category><category>ftp</category><category>full</category><category>fullResolutionImage</category><category>ga3</category><category>galaxy</category><category>gateway</category><category>gb2312</category><category>gcc</category><category>gem</category><category>gemini-pro</category><category>gender</category><category>genpkey</category><category>geo</category><category>geodb</category><category>geolite2</category><category>getActionProvider</category><category>get_dummies</category><category>gguf</category><category>gin</category><category>github copilot</category><category>gitlab duo</category><category>gitlab.com</category><category>gitlab.example.com</category><category>glances</category><category>glyph</category><category>glyph runs</category><category>gmt</category><category>gnutls</category><category>gnutls_handshake</category><category>go-astilectron</category><category>go-astilectron-bootstrap</category><category>go-astilectron-bundler</category><category>goal</category><category>google adsense</category><category>google analysis</category><category>google api</category><category>google charts</category><category>google cloud ai study jam</category><category>google spreadsheet</category><category>google 支援小組</category><category>google-play-services_lib</category><category>google_play_services_version</category><category>googltest</category><category>gpg</category><category>group by</category><category>gsutil</category><category>gtag.js</category><category>guess</category><category>gunzip</category><category>gzip</category><category>hack</category><category>handler</category><category>hardware</category><category>hash</category><category>hashchange</category><category>hdcp</category><category>head</category><category>health</category><category>health check</category><category>height</category><category>hexdump</category><category>hf</category><category>hide</category><category>high</category><category>hot reload</category><category>hotspot</category><category>href</category><category>http api</category><category>http request</category><category>http.server</category><category>huggingface</category><category>hugo</category><category>icloud</category><category>icmp</category><category>idtoken</category><category>if</category><category>iframe</category><category>igmp</category><category>image2epub</category><category>imagemagick</category><category>immich</category><category>impress</category><category>in</category><category>in review</category><category>incremental</category><category>indicator</category><category>influxDB</category><category>inner</category><category>input</category><category>inspector</category><category>intel</category><category>interface builder</category><category>internal compiler error</category><category>internet</category><category>interstitial ads</category><category>inventory</category><category>ios7</category><category>ios8</category><category>ipad 8</category><category>iptable</category><category>ipv6</category><category>iqiyi</category><category>issue</category><category>it</category><category>itanic</category><category>jam</category><category>jdk</category><category>js engine</category><category>js injection</category><category>json_decode</category><category>json_last_error</category><category>jsonp</category><category>jsonrpc</category><category>jubler</category><category>keyboard</category><category>keypair</category><category>kubeadm</category><category>kvm</category><category>lambda</category><category>lang</category><category>language</category><category>laurel</category><category>laurel_sprout</category><category>legacy</category><category>let</category><category>lftp</category><category>libevent</category><category>libudev-dev</category><category>limits</category><category>line_ending</category><category>lines</category><category>links</category><category>llama3.1</category><category>llm</category><category>load</category><category>load balancer</category><category>loadData</category><category>loadRequest</category><category>loading</category><category>local</category><category>localhost.crt</category><category>localhost.key</category><category>localtime</category><category>location.replace</category><category>lock</category><category>log(1+x)</category><category>loopback</category><category>low</category><category>lsep</category><category>lte</category><category>lxc-attach</category><category>m1 pro</category><category>m3u8</category><category>mDNS</category><category>mac mini</category><category>macmin</category><category>mail app</category><category>mailparser</category><category>make</category><category>manage</category><category>manager</category><category>mapper</category><category>mapreduce</category><category>maps</category><category>markdown</category><category>marketing</category><category>mask</category><category>matomo</category><category>matplot</category><category>matplotlib</category><category>mau</category><category>max</category><category>mbp</category><category>mediatek</category><category>member</category><category>memcache</category><category>message</category><category>meta</category><category>metal visa card</category><category>metasearch</category><category>microSD</category><category>microsoft</category><category>migration</category><category>min</category><category>minetype</category><category>miui</category><category>ml</category><category>mobile app</category><category>mod_geoip</category><category>mode</category><category>monetize</category><category>mongoose</category><category>mongos</category><category>monospace</category><category>monospaced</category><category>movie</category><category>mpm</category><category>mtk</category><category>mua</category><category>multicast</category><category>multiply</category><category>mutt</category><category>my.cnf</category><category>mysql5.7</category><category>mysqld</category><category>namecheap.com</category><category>namespace</category><category>nat</category><category>nc</category><category>net/http</category><category>netflow</category><category>ng-src</category><category>nginx-module-geoip</category><category>nib</category><category>nicstat</category><category>no resource found</category><category>no-reply</category><category>non-consumable</category><category>nonblocking</category><category>noreply</category><category>nosql</category><category>npx</category><category>nrpe</category><category>nsenter</category><category>nslookup</category><category>ntp</category><category>ntpdate</category><category>numpy</category><category>nvm</category><category>nw_protocol_boringssl_input_finished</category><category>obj-c</category><category>objective-c</category><category>occurrence</category><category>od</category><category>office</category><category>offset</category><category>old</category><category>one</category><category>one-hot encode</category><category>oneplus</category><category>online</category><category>op</category><category>open</category><category>open source</category><category>openai</category><category>openjdk</category><category>openldap</category><category>openoffice</category><category>openstreetmap</category><category>openswan</category><category>openvpn</category><category>oracle</category><category>order</category><category>osticker</category><category>out of memory</category><category>outlook</category><category>output</category><category>pack</category><category>package</category><category>page</category><category>page level</category><category>pagination</category><category>panels</category><category>papermod</category><category>paragraphs</category><category>params</category><category>parse</category><category>partition</category><category>passing by reference</category><category>passphrase</category><category>patch</category><category>path</category><category>pattern</category><category>pc app</category><category>pcloudcc</category><category>pdf2epub</category><category>peak</category><category>pem</category><category>percentage</category><category>performSegueWithIdentifier</category><category>perlbrew</category><category>permisions</category><category>permission</category><category>phabricator</category><category>phone</category><category>photo</category><category>php-7.4</category><category>php7.2</category><category>php7.4</category><category>php71</category><category>php74</category><category>phpbb3</category><category>phpunit</category><category>picker</category><category>pip</category><category>playlist</category><category>plus</category><category>png</category><category>podcast</category><category>pointer</category><category>pop</category><category>port</category><category>portal</category><category>ports</category><category>postgre</category><category>postgresql</category><category>powerpoint</category><category>powersheel</category><category>pptp</category><category>pptv</category><category>precision_score</category><category>prefork</category><category>preg_match_all</category><category>prerouting</category><category>private</category><category>private ip</category><category>problem</category><category>proc</category><category>process</category><category>profile</category><category>project</category><category>prompt</category><category>promptpack-for-code</category><category>protect</category><category>provider</category><category>ps</category><category>psql</category><category>pub.dev</category><category>public ip</category><category>publish</category><category>publish_actions</category><category>puffin</category><category>puffin os</category><category>pull</category><category>purge</category><category>push notification</category><category>put</category><category>pyicap</category><category>pyplot</category><category>qemu</category><category>qq</category><category>qqmail</category><category>quality</category><category>quicksetup</category><category>quota</category><category>r1000</category><category>random</category><category>range</category><category>rate</category><category>raw</category><category>raw socket</category><category>re</category><category>re-key</category><category>re2</category><category>reboot</category><category>recall_score</category><category>recommendation</category><category>record</category><category>recovery</category><category>redhat</category><category>redirect</category><category>reducer</category><category>reflection</category><category>refresh</category><category>regular expression</category><category>reject</category><category>relation</category><category>relpace</category><category>remote desktop</category><category>remote ip</category><category>replSet</category><category>request timeout</category><category>rest api</category><category>restful</category><category>restore to</category><category>rfc822</category><category>rights</category><category>rm</category><category>robin</category><category>roc</category><category>roc_auc</category><category>roc_curve</category><category>rom</category><category>rootViewController</category><category>rotate</category><category>round</category><category>route53</category><category>router</category><category>routine</category><category>row</category><category>rpc</category><category>rr</category><category>rsync</category><category>rtl8821cu</category><category>ru_maxrss</category><category>ruby</category><category>rule</category><category>rules</category><category>run</category><category>rusage</category><category>s3fs</category><category>sample</category><category>sandisk</category><category>satisfy</category><category>scaling</category><category>scan</category><category>scapy</category><category>scikit-learn</category><category>screenview</category><category>scrip</category><category>scrollback</category><category>seaborn</category><category>search</category><category>secretsmanager</category><category>sedo.com</category><category>segue</category><category>selectattr</category><category>self</category><category>self-update</category><category>semaphore</category><category>sendgrid</category><category>sensitive</category><category>seo</category><category>servers</category><category>services</category><category>session</category><category>sha256</category><category>shard</category><category>shared libraries</category><category>sharing</category><category>sheet</category><category>sheets</category><category>shell</category><category>shift</category><category>shopify</category><category>show</category><category>shown</category><category>simplexml</category><category>simplexml_load_string</category><category>single-node</category><category>site</category><category>site_url</category><category>sitemap</category><category>size</category><category>sketchbook</category><category>skill</category><category>skip ssl</category><category>sleep</category><category>snapshot</category><category>snooping</category><category>social</category><category>socket</category><category>socket_vmnet</category><category>space</category><category>spec</category><category>spider</category><category>sql-test</category><category>squid</category><category>src</category><category>ssh tunnel</category><category>sshd</category><category>ssl check</category><category>ssl_certificate</category><category>ssl_certificate_key</category><category>standalone</category><category>starttls</category><category>state</category><category>std</category><category>stdout</category><category>store</category><category>strace</category><category>streaming</category><category>strtotime</category><category>struct</category><category>structure</category><category>studio</category><category>style</category><category>subdir</category><category>subdomain</category><category>subdomain count</category><category>sum</category><category>summit</category><category>supervisor</category><category>support</category><category>svg</category><category>svm</category><category>svn</category><category>swipe</category><category>symbolic</category><category>syntax error</category><category>sysctl</category><category>systcl</category><category>system time</category><category>systemsetup</category><category>tabnine</category><category>tag</category><category>taipei 101</category><category>taiwansocialmarketing</category><category>tap</category><category>tcp</category><category>telegram</category><category>theme</category><category>third-party</category><category>thttpd</category><category>thumb</category><category>thumbnail</category><category>tidy</category><category>timeline</category><category>timezome</category><category>tk</category><category>tkinter</category><category>tls1.2</category><category>to_entries</category><category>toolchain</category><category>tools</category><category>top</category><category>top n</category><category>topic</category><category>tr</category><category>tracking id</category><category>translator</category><category>tree</category><category>tts</category><category>tty</category><category>turn off</category><category>twill</category><category>twillcms</category><category>type</category><category>u2028</category><category>ubuntu16.04</category><category>ubuntu18.04</category><category>uclibc</category><category>uclibc-ng</category><category>udid</category><category>uitableview</category><category>uitextalignmentcenter</category><category>uitextalignmentleft</category><category>uitextalignmentright</category><category>uiwebview</category><category>unicode</category><category>uniq</category><category>unittest</category><category>unordered_map</category><category>upnp</category><category>upstream</category><category>uri</category><category>urllib2</category><category>urlopen</category><category>utc</category><category>utm</category><category>uwsgi</category><category>v1.18</category><category>v2</category><category>v2.2</category><category>v2.8</category><category>v20</category><category>v22</category><category>v4</category><category>v5</category><category>value</category><category>vars</category><category>vector</category><category>verification</category><category>version</category><category>video</category><category>vimrc</category><category>virsh</category><category>virtualbox</category><category>virtualnetwork</category><category>virus</category><category>vnstat</category><category>volumes</category><category>wakeonlan</category><category>wasmer</category><category>wau</category><category>wd</category><category>web app</category><category>web page</category><category>webView:didFinishNavigation:</category><category>webView:didStartProvisionalNavigation:</category><category>webpack-dev-server</category><category>website</category><category>wechat</category><category>when</category><category>wifi ap</category><category>wifidog</category><category>wiki</category><category>wildcard ssl</category><category>wireshark</category><category>without</category><category>wol</category><category>word count</category><category>working</category><category>ws</category><category>wsgi</category><category>wss</category><category>wss://ws.ptt.cc/bbs</category><category>www</category><category>www-data</category><category>x64</category><category>x86</category><category>x86_64</category><category>xCloud</category><category>xcode 6</category><category>xgboost</category><category>xmllint</category><category>xpi</category><category>yaml</category><category>yml</category><category>youtube-dl</category><category>youtube.py</category><category>youtuber</category><category>上架</category><category>中文</category><category>事件</category><category>人力成本</category><category>付款收據</category><category>保險</category><category>借我一下</category><category>側耳傾聽</category><category>優點</category><category>免治馬桶</category><category>免費</category><category>兒童相機</category><category>公視+</category><category>共現矩陣</category><category>出租女友</category><category>利息</category><category>刻意練習</category><category>動漫</category><category>動畫</category><category>匯入</category><category>印表機</category><category>原聲帶</category><category>台灣社群行銷年會</category><category>喇叭</category><category>嘖嘖</category><category>團拜</category><category>塑膠</category><category>壓克力</category><category>女朋友</category><category>存取</category><category>學齡</category><category>完整清除</category><category>完整移除</category><category>富邦</category><category>富邦產險</category><category>實體店</category><category>寵物</category><category>導航王</category><category>小米A3</category><category>小米便攜相片印表機</category><category>尺寸</category><category>巨集</category><category>帳戶問題</category><category>平均</category><category>平均員工薪資</category><category>幸福路上</category><category>廣告投放</category><category>廣告放送量受限</category><category>建立</category><category>影像擷取卡</category><category>影像采集卡</category><category>彼得原理</category><category>從0到1</category><category>心之谷</category><category>心得</category><category>心理師</category><category>快篩試劑</category><category>拯救</category><category>排除</category><category>插頁廣告</category><category>揚聲器</category><category>操作IC</category><category>收信</category><category>政策中心</category><category>救援筆記</category><category>斜槓青年</category><category>方式</category><category>智慧直流變頻電風扇</category><category>楓林網</category><category>樊登說書</category><category>樹莓派</category><category>機車</category><category>檔案大小</category><category>檢視憑證</category><category>流量池</category><category>海潮之聲</category><category>淘寶</category><category>滑倒</category><category>無效流量</category><category>無效流量疑慮</category><category>無框行動</category><category>爬蟲</category><category>玉山金</category><category>玉山銀行</category><category>現代PHP</category><category>理賠</category><category>瑞幸咖啡</category><category>生成式 AI</category><category>生活小知識</category><category>申請</category><category>畫質</category><category>疫苗險</category><category>發票</category><category>白燈一直閃</category><category>白燈閃爍</category><category>目標</category><category>目標主體名稱不正確</category><category>目錄</category><category>直營</category><category>相片</category><category>相簿</category><category>眾籌</category><category>碎碎念</category><category>租借女友</category><category>立燈</category><category>第三代</category><category>第二代</category><category>筆電</category><category>筆電架</category><category>紳士的品格</category><category>維修</category><category>網路購買</category><category>網路電視</category><category>網頁</category><category>線上</category><category>縮圖</category><category>缺點</category><category>聯發科</category><category>聲音</category><category>股癌</category><category>脫膜</category><category>脫膠</category><category>膨脹</category><category>蕭敬騰</category><category>薪資</category><category>藍光</category><category>藍牙耳機</category><category>處理</category><category>西門</category><category>規格</category><category>解救</category><category>觸控筆</category><category>記憶卡損毀</category><category>設定</category><category>評估</category><category>認識的妻子</category><category>請輸入檢索詞WWW</category><category>謝孟恭</category><category>證交所</category><category>讀後感</category><category>資料</category><category>資方</category><category>質押</category><category>購買</category><category>跨境</category><category>輕薄</category><category>轉卡</category><category>返回18歲</category><category>遊戲</category><category>過保可丟</category><category>達哥</category><category>還原</category><category>鄉村路</category><category>鄉鎮市區</category><category>重灌</category><category>銷售</category><category>鎖幣</category><category>閱讀</category><category>閱讀器</category><category>關鍵字</category><category>防塵</category><category>防塵盒</category><category>防塵箱</category><category>防水性極佳</category><category>防水鞋</category><category>阿里雲</category><category>陳寗</category><category>隔離險</category><category>隱形筆電架</category><category>零規則</category><category>電子書</category><category>電影</category><category>電源燈閃爍</category><category>電視盒</category><category>鞋底設計</category><category>音控</category><category>魂囚西門</category><category>黏黏的</category><category>검색어를입력하세요WWW</category><title>第二十四個夏天後</title><description>幸福，不是做自己喜歡的事，而是喜歡自己做的事</description><link>http://blog.changyy.org/</link><managingEditor>noreply@blogger.com (Unknown)</managingEditor><generator>Blogger</generator><openSearch:totalResults>1645</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><language>en-us</language><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-7754332811135291706</guid><pubDate>Wed, 11 Mar 2026 14:27:00 +0000</pubDate><atom:updated>2026-03-17T15:55:25.937+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">m4</category><category domain="http://www.blogger.com/atom/ns#">MAC</category><category domain="http://www.blogger.com/atom/ns#">macmin</category><category domain="http://www.blogger.com/atom/ns#">macstrap</category><category domain="http://www.blogger.com/atom/ns#">OpenClaw</category><category domain="http://www.blogger.com/atom/ns#">quicksetup</category><category domain="http://www.blogger.com/atom/ns#">telegram</category><title>[macos] 快速設定 Mac Mini M4 32GB/1TB 並安裝設定 OpenClaw + Telegram + FileVault 排錯 @ Macbook M1</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinhpmA8k1BHwauTfYkGzMlE1I5GTjFAhiiKiHSS0k9CDY8UHZX-NANwC8vrV9X0BfXQ-7Ooju-dJ7hOsQR9pQWuMRavpWYBHn8SgA1xaGv-o0R9yU3LjQnsYYZ0rhZdMad8o-vvVekvpb5BMuZSVjtsQpJHGOigHoZaSlmYHWIgqDwUPzlTIv_DE4mfbjV/s2306/openclaw-dashboard.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1634" data-original-width="2306" height="454" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinhpmA8k1BHwauTfYkGzMlE1I5GTjFAhiiKiHSS0k9CDY8UHZX-NANwC8vrV9X0BfXQ-7Ooju-dJ7hOsQR9pQWuMRavpWYBHn8SgA1xaGv-o0R9yU3LjQnsYYZ0rhZdMad8o-vvVekvpb5BMuZSVjtsQpJHGOigHoZaSlmYHWIgqDwUPzlTIv_DE4mfbjV/w640-h454/openclaw-dashboard.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;昨天弄出一個簡單的工具 &lt;a href="https://pypi.org/project/macstrap/" rel="nofollow" target="_blank"&gt;macstrap&lt;/a&gt; ，方便未來快速批次處理 mac mini M4。然而，mac mini M4 一開始還是有幾個步驟需要人工處理的。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;初次把 Mac mini 開機，過程會設定網路連線，以及設定一組帳密登入。進入系統後：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;Sound: 聲音調成靜音&lt;/li&gt;&lt;li&gt;Energy: 開啟避免自動睡眠、電源失敗後可自動開機、網路喚醒&lt;/li&gt;&lt;li&gt;General: Sharing -&amp;gt; Remote Login: On&lt;/li&gt;&lt;li&gt;General: Sharing -&amp;gt; Remote Management: On -&amp;gt; 選項全開, 密碼設置, 指定使用者使用且選項全開&lt;/li&gt;&lt;li&gt;General: About -&amp;gt; Name -&amp;gt; 可以改成簡短的名稱，如 mac-mini ，好處就是可以用 ssh mac-mini.local 連線&lt;/li&gt;&lt;li&gt;General: Users &amp;amp; Group -&amp;gt; Automatically log in as -&amp;gt; user&lt;/li&gt;&lt;li&gt;開啟 termnial -&amp;gt; 輸入 python3 -&amp;gt; 會觸動透過 Xcode 安裝 python3 環境&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeG8ZAWgDmMDh11PQ81TPNJ-0WMd40ByMcDANZkQod2Do1mSZ0vwJkodx7T2sdo-xNv6s_4WIg7xibAb5RtfzivXwjtKQjK30L0YAT0Wc3ETm7L9Q1hzic2043ZoWzav9VlyXMfp6r8iV-q6JzhL3IMCoe0PaaDIu3gFSCFIDZW8SE1GmGcTnRtCji2lRc/s1500/xcode-python3.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="882" data-original-width="1500" height="376" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeG8ZAWgDmMDh11PQ81TPNJ-0WMd40ByMcDANZkQod2Do1mSZ0vwJkodx7T2sdo-xNv6s_4WIg7xibAb5RtfzivXwjtKQjK30L0YAT0Wc3ETm7L9Q1hzic2043ZoWzav9VlyXMfp6r8iV-q6JzhL3IMCoe0PaaDIu3gFSCFIDZW8SE1GmGcTnRtCji2lRc/w640-h376/xcode-python3.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如此就完成 mac mini 可被遠端操作的基本設定了，可以測試：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;ssh user@mac-mini.local&amp;nbsp;&lt;/li&gt;&lt;li&gt;開啟 螢幕共享 -&amp;gt; 輸入 mac-mini.local -&amp;gt; 選擇帳號登入&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;以上兩種就是可遠端的配置了，而多了 python3 的安裝，是為了用 macstrap 快速安置開發環境&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% mkdir openclaw-on-mac-mini &amp;amp;&amp;amp; cd openclaw-on-mac-mini&lt;/div&gt;&lt;div&gt;% python3 -m venv venv &amp;amp;&amp;amp; source venv/bin/activate &amp;amp;&amp;amp; pip install macstrap&lt;/div&gt;&lt;div&gt;% macstrap init --examples&lt;/div&gt;&lt;div&gt;% macstrap ssh-auth mac-mini.local --user user&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;✓&amp;nbsp; Passwordless SSH to mac-mini.local working.&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;✓&amp;nbsp; Sudo password stored in macOS Keychain&lt;/div&gt;&lt;div&gt;% macstrap run --config examples/ai-cli --config examples/openclaw&amp;nbsp; mac-mini.local&amp;nbsp;&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如此系統就會安裝一些常見的 AI cli (OpenAI - Codex CLI, Google - Gemini CLI, Microsoft - Copilot CLI), Openclaw 跟 Docker Desktop 也安裝好了，而 Docker Desktop 需要登入 macOS 上先來個手動啟動，啟動後可以在設定區開啟 Start Docker Desktop when you sign into your computer，而這台 mac mini M4 是 32GB RAM 跟 1TB SSD ，因此可以多善用 Docker 來封裝一些任務做風險管控。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著就是啟動 openclaw 的部分了，可以在 macos Desktop 啟動，也可以用 ssh 登入啟動，在此用後者：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% ssh user@mac-mini.local&lt;/div&gt;&lt;div&gt;user@mac-mini ~ % openclaw onboard&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&#129438; OpenClaw 2026.3.8 (3caab92)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;I'm not AI-powered, I'm AI-possessed. Big difference.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄&lt;/div&gt;&lt;div&gt;██░▄▄▄░██░▄▄░██░▄▄▄██░▀██░██░▄▄▀██░████░▄▄▀██░███░██&lt;/div&gt;&lt;div&gt;██░███░██░▀▀░██░▄▄▄██░█░█░██░█████░████░▀▀░██░█░█░██&lt;/div&gt;&lt;div&gt;██░▀▀▀░██░█████░▀▀▀██░██▄░██░▀▀▄██░▀▀░█░██░██▄▀▄▀▄██&lt;/div&gt;&lt;div&gt;▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#129438; OPENCLAW &#129438;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;┌&amp;nbsp; OpenClaw onboarding&lt;/div&gt;&lt;div&gt;│...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;我們採用 QuickStart ，以及 Copilot (Github + local proxy) ，而 Copilot CLI 認證方式採用 `GitHub Copilot (GitHub device login) (Uses GitHub device flow)`，且模型就挑 `GPT-5 mini` 好了&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;主因是 Github Copilot 在 2026-03-11 的模型費用：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;&amp;nbsp;Search models...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; Claude Sonnet 4.5 (default) ✓&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1x&lt;/div&gt;&lt;div&gt;&amp;nbsp; Claude Haiku 4.5&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 0.33x&lt;/div&gt;&lt;div&gt;&amp;nbsp; Claude Opus 4.6&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 3x&lt;/div&gt;&lt;div&gt;&amp;nbsp; Claude Opus 4.6 (fast mode) (Preview) (requires enablement)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;30x&lt;/div&gt;&lt;div&gt;&amp;nbsp; Claude Opus 4.5&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 3x&lt;/div&gt;&lt;div&gt;&amp;nbsp; Claude Sonnet 4&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1x&lt;/div&gt;&lt;div&gt;&amp;nbsp; Gemini 3 Pro (Preview)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1x&lt;/div&gt;&lt;div&gt;&amp;nbsp; GPT-5.3-Codex&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1x&lt;/div&gt;&lt;div&gt;&amp;nbsp; GPT-5.2-Codex&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1x&lt;/div&gt;&lt;div&gt;&amp;nbsp; GPT-5.2&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1x&lt;/div&gt;&lt;div&gt;&amp;nbsp; GPT-5.1-Codex-Max&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1x&lt;/div&gt;&lt;div&gt;&amp;nbsp; GPT-5.1-Codex&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1x&lt;/div&gt;&lt;div&gt;&amp;nbsp; GPT-5.1&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1x&lt;/div&gt;&lt;div&gt;&amp;nbsp; GPT-5.1-Codex-Mini (Preview)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 0.33x&lt;/div&gt;&lt;div&gt;❯ GPT-5 mini&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;0x&lt;/div&gt;&lt;div&gt;&amp;nbsp; GPT-4.1&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 0x&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;若表現太差就換到 `Claude Sonnet 4.5`&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著就是跟龍蝦溝通的方式 `Select channel (QuickStart)`&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;◆&amp;nbsp; Select channel (QuickStart)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ● Telegram (Bot API) (recommended · newcomer-friendly)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ WhatsApp (QR link)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Discord (Bot API)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ IRC (Server + Nick)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Google Chat (Chat API)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Slack (Socket Mode)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Signal (signal-cli)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ iMessage (imsg)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ LINE (Messaging API)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Feishu/Lark (飞书)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Nostr (NIP-04 DMs)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Microsoft Teams (Bot Framework)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Mattermost (plugin)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Nextcloud Talk (self-hosted)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Matrix (plugin)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ BlueBubbles (macOS app)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Zalo (Bot API)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Zalo (Personal Account)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Synology Chat (Webhook)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Tlon (Urbit)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Skip for now&lt;/div&gt;&lt;div&gt;└&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;就是一下 Telegram 吧，設定滿簡單的，只要到 Telegram BotFather 上註冊一個 Bot 即可，過程會給予 HTTP API token ，回 OpenClaw 貼上即可。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著 Install missing skill dependencies 的部分，就挑一下 macOS 系列: apple-notes, apple-reminders, openai-whisper 等等:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;◆&amp;nbsp; Install missing skill dependencies&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ Skip for now (Continue without installing dependencies)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128272; 1password&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◼ &#128221; apple-notes (Manage Apple Notes via the `memo` CLI on macOS&amp;nbsp;&lt;/div&gt;&lt;div&gt;│&amp;nbsp; (create, view, edit, delete, search, move,…)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◼ ⏰ apple-reminders (Manage Apple Reminders via remindctl CLI&amp;nbsp;&lt;/div&gt;&lt;div&gt;│&amp;nbsp; (list, add, edit, complete, delete). Supports li…)&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128059; bear-notes&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128240; blogwatcher&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#129744; blucli&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128248; camsnap&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#129513; clawhub&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#127899;️ eightctl&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#129522; gifgrep&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#127918; gog&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128205; goplaces&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128231; himalaya&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128232; imsg&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128230; mcporter&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128202; model-usage&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#127820; nano-banana-pro&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128196; nano-pdf&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128142; obsidian&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◼ &#127897;️ openai-whisper (Local speech-to-text with the Whisper CLI (no&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;API key). — Install OpenAI Whisper (brew))&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128161; openhue&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#129535; oracle&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128757; ordercli&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128064; peekaboo&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128483;️ sag&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#127754; songsee&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128266; sonoscli&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#129534; summarize&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ ✅ things-mac&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#127902;️ video-frames&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#128241; wacli&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ◻ &#120143; xurl&lt;/div&gt;&lt;div&gt;└&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著 hooks 就全裝，最後就差不多搞定了。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;未來想要打開設定，可以改用 openclaw dashboard 指令，將允許在 http://localhost:12345/ 類似的網址瀏覽。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這邊提一下 telegram 連線有問題的處理方式：&lt;/div&gt;&lt;div&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;先在 Telegram BotFather 那邊查看 Bot 的情況，像是用 `/mybots` ，接著可以查看指定 bot 的 api token&lt;/li&gt;&lt;li&gt;直接在 mac mini 上編輯 ~/.openclaw/openclaw.json 檔案，確認：&lt;/li&gt;&lt;ul&gt;&lt;li&gt;查看 channels.telegram 的設定，如 enabled: true, dmPolicy: pairing, 以及可以順便更新一下 botToken 等&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;在 mac mini 使用指令 `openclaw gateway` 運行&lt;/li&gt;&lt;li&gt;在 Telegram Bot 上，打 `/start` 指令&lt;/li&gt;&lt;li&gt;在 mac mini 使用指令 `openclaw pairing list telegram`&lt;/li&gt;&lt;li&gt;如果有看到配對碼，就用 `openclaw pairing approve telegram &amp;lt;CODE&amp;gt;` 生效他&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;如果 mac mini 重啟後，容易 Telegram 沒反應，這時可以用 ssh user@mac-mini.local 試試，若會看到 `System successfully unlocked.&amp;nbsp;You may now use SSH to authenticate normally.` ，大概是因為&amp;nbsp;FileVault 的關係，可以評估一下是否關閉：&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;&lt;div&gt;user@mac-mini ~ % fdesetup status&lt;/div&gt;&lt;div&gt;FileVault is On.&lt;/div&gt;&lt;div&gt;user@mac-mini ~ % sudo fdesetup disable&lt;/div&gt;&lt;div&gt;Enter the user name:user&lt;/div&gt;&lt;div&gt;Enter the password for user 'user':&lt;/div&gt;&lt;div&gt;FileVault has been disabled.&lt;/div&gt;&lt;div&gt;user@mac-mini ~ % fdesetup status&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;FileVault is Off.&lt;/div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;最後，設定一下開機自動執行 openclaw ，這邊單純用 crontab 管理，還有其他更佳的方案，之後再來研究：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;user@mac-mini ~ % which openclaw&lt;/div&gt;&lt;div&gt;/Users/user/.nvm/versions/node/v22.22.1/bin/openclaw&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;user@mac-mini ~ % crontab -l&lt;/div&gt;&lt;div&gt;@reboot PATH=$PATH:$HOME/.nvm/versions/node/v22.22.1/bin/ $HOME/.nvm/versions/node/v22.22.1/bin/openclaw gateway &amp;gt;&amp;gt; $HOME/openclaw.log 2&amp;gt;&amp;amp;1&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;</description><link>http://blog.changyy.org/2026/03/mac-mini-m4-32gb-1tb-quick-setup-openclaw-with-telegram-support-reboot.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinhpmA8k1BHwauTfYkGzMlE1I5GTjFAhiiKiHSS0k9CDY8UHZX-NANwC8vrV9X0BfXQ-7Ooju-dJ7hOsQR9pQWuMRavpWYBHn8SgA1xaGv-o0R9yU3LjQnsYYZ0rhZdMad8o-vvVekvpb5BMuZSVjtsQpJHGOigHoZaSlmYHWIgqDwUPzlTIv_DE4mfbjV/s72-w640-h454-c/openclaw-dashboard.jpg" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-2792714782911042330</guid><pubDate>Tue, 10 Mar 2026 14:19:00 +0000</pubDate><atom:updated>2026-03-10T22:34:09.563+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cicd</category><category domain="http://www.blogger.com/atom/ns#">devops</category><category domain="http://www.blogger.com/atom/ns#">m4</category><category domain="http://www.blogger.com/atom/ns#">MAC</category><category domain="http://www.blogger.com/atom/ns#">mac mini</category><category domain="http://www.blogger.com/atom/ns#">macos</category><category domain="http://www.blogger.com/atom/ns#">macstrap</category><category domain="http://www.blogger.com/atom/ns#">nvm</category><category domain="http://www.blogger.com/atom/ns#">OpenClaw</category><title>[macOS] 使用 macstrap 批次快速設定 Mac Mini M4 與架設 OpenClaw 環境 @ macOS</title><description>&lt;div&gt;最近拿到一台 Mac mini M4 32GB/1TB ，之前很習慣用 Notes 貼一下快速安裝的指令，想著想著自己十多年前都在用 ansible 管理百台 Ubuntu servers ，這個年代一直貼指令微遜 XD 就 vibe coding 一下，請 AI 小弟幫忙實現工具，於是乎就長出個 macstrap 工具，其特色就是讓不熟悉 ansible 的人也能快速上手，而安裝途徑是透過 ssh remote command ，這樣搞定一台後，未來有多台 mac mini 要初始化，也可以快速搞定。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;首先，先把 Mac mini 開箱後，設定好登入的帳號密碼，接著在 System Settings -&amp;gt; General -&amp;gt; Sharing -&amp;gt; Remote login 打開，這樣就完成很基本的 ssh 可登入機制，然而 macstrap 是基於 python 的工具，所以 Mac mini 本地需要故意打一下 python 指令觸發系統去下載安裝 Python3&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接下來就是 macstrap 領域了：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% cd /tmp&lt;/div&gt;&lt;div&gt;% python3 -m venv venv&lt;/div&gt;&lt;div&gt;% source /tmp/venv/bin/activate&lt;/div&gt;&lt;div&gt;% pip install macstrap&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;% macstrap --version&amp;nbsp;&lt;/div&gt;&lt;div&gt;macstrap, version 1.1.0&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;初始化一些範例來用：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% macstrap init --exmaples&lt;/div&gt;&lt;div&gt;% tree -L 1 examples&lt;/div&gt;&lt;div&gt;examples&lt;/div&gt;&lt;div&gt;├── ai-cli&lt;/div&gt;&lt;div&gt;├── openclaw&lt;/div&gt;&lt;div&gt;├── php8.3-dev&lt;/div&gt;&lt;div&gt;└── utilities-dev&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;5 directories, 0 files&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;設置連上 Mac mini 的帳密，假設 Mac mini IP 是 192.168.1.100 (或 mac-mini.local) 登入帳號是 macuser 且擁有 sudo 權限：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% macstrap ssh-auth mac-mini.local --user macuser&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;替 192.168.1.100 (或 mac-mini.local) 安裝 AI CLI:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% macstrap run --config examples/ai-cli mac-mini.local&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;TASK [nvm : Install global npm packages] *****&lt;/div&gt;&lt;div&gt;ok: [mac-mini.local] =&amp;gt; (item=@github/copilot)&lt;/div&gt;&lt;div&gt;ok: [mac-mini.local] =&amp;gt; (item=@anthropic-ai/claude-code)&lt;/div&gt;&lt;div&gt;ok: [mac-mini.local] =&amp;gt; (item=@openai/codex)&lt;/div&gt;&lt;div&gt;ok: [mac-mini.local] =&amp;gt; (item=@google/gemini-cli)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;安裝 OpenClaw:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% macstrap run --config examples/openclaw mac-mini.local&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;TASK [nvm : Install global npm packages] *****&lt;/div&gt;&lt;div&gt;ok: [user-macmini.local] =&amp;gt; (item=openclaw)&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著登入 mac-mini.local 後，就可以用 openclaw onboard 啟動他來做設定：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% ssh macuser@mac-mini.local&lt;/div&gt;&lt;div&gt;Now using node v22.22.1 (npm v10.9.4)&lt;/div&gt;&lt;div&gt;[env] node: v22.22.1 | npm: 10.9.4 | Python 3.9.6&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;% openclaw onboard&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&#129438; OpenClaw 2026.3.8 (3caab92)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;Runs on a Raspberry Pi. Dreams of a rack in Iceland.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄&lt;/div&gt;&lt;div&gt;██░▄▄▄░██░▄▄░██░▄▄▄██░▀██░██░▄▄▀██░████░▄▄▀██░███░██&lt;/div&gt;&lt;div&gt;██░███░██░▀▀░██░▄▄▄██░█░█░██░█████░████░▀▀░██░█░█░██&lt;/div&gt;&lt;div&gt;██░▀▀▀░██░█████░▀▀▀██░██▄░██░▀▀▄██░▀▀░█░██░██▄▀▄▀▄██&lt;/div&gt;&lt;div&gt;▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#129438; OPENCLAW &#129438;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;┌&amp;nbsp; OpenClaw onboarding&lt;/div&gt;&lt;div&gt;│&lt;/div&gt;&lt;div&gt;◇&amp;nbsp; Security ─────────────────────────────────────────────────────────────╮&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; Security warning — please read.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; OpenClaw is a hobby project and still in beta. Expect sharp edges.&amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; By default, OpenClaw is a personal agent: one trusted operator&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; boundary.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; This bot can read files and run actions if tools are enabled.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; A bad prompt can trick it into doing unsafe things.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; OpenClaw is not a hostile multi-tenant boundary by default.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; If multiple users can message one tool-enabled agent, they share&amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; that delegated tool authority.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; If you’re not comfortable with security hardening and access&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; control, don’t run OpenClaw.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; Ask someone experienced to help before enabling tools or exposing it&amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; to the internet.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; Recommended baseline:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; - Pairing/allowlists + mention gating.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; - Multi-user/shared inbox: split trust boundaries (separate&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; gateway/credentials, ideally separate OS users/hosts).&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; - Sandbox + least-privilege tools.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; - Shared inboxes: isolate DM sessions (`session.dmScope:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; per-channel-peer`) and keep tool access minimal.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; - Keep secrets out of the agent’s reachable filesystem.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; - Use the strongest available model for any bot with tools or&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; untrusted inboxes.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; Run regularly:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; openclaw security audit --deep&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; openclaw security audit --fix&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; Must read: https://docs.openclaw.ai/gateway/security&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;├────────────────────────────────────────────────────────────────────────╯&lt;/div&gt;&lt;div&gt;│&lt;/div&gt;&lt;div&gt;◆&amp;nbsp; I understand this is personal-by-default and shared/multi-user use requires&lt;/div&gt;&lt;div&gt;&amp;nbsp;lock-down. Continue?&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Yes / ● No&lt;/div&gt;&lt;div&gt;└&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;</description><link>http://blog.changyy.org/2026/03/macos-macstrap-mac-mini-m4-openclaw.html</link><author>noreply@blogger.com (Unknown)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-7713516847668452378</guid><pubDate>Fri, 13 Feb 2026 10:26:00 +0000</pubDate><atom:updated>2026-02-13T18:26:00.116+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Debian 12</category><category domain="http://www.blogger.com/atom/ns#">OpenClaw</category><category domain="http://www.blogger.com/atom/ns#">Pi 3</category><category domain="http://www.blogger.com/atom/ns#">Raspberry</category><title>[Linux] 安裝 OpenClaw @ raspberrypi Debian 12</title><description>&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmzkzSADFVQvY4kzlK-nFF0jMTuYB6PjB2bhiJ_ttVVrmAvLMDPuzPLC4_RcDIdijxIGcVAyZjz9EEV5-HQDBE6c3JEDaUDMqjsKEJ8IEo7o6Oon2t24cKdr3FbKY1d3vjNybnhx_pLwJNEbvahZ0euHI7NUET5mPQahLZs_uZEeQuQkGU00T9RJ3XHLok/s1598/%E6%88%AA%E5%9C%96%202026-02-12%2017.13.06.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1320" data-original-width="1598" height="528" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmzkzSADFVQvY4kzlK-nFF0jMTuYB6PjB2bhiJ_ttVVrmAvLMDPuzPLC4_RcDIdijxIGcVAyZjz9EEV5-HQDBE6c3JEDaUDMqjsKEJ8IEo7o6Oon2t24cKdr3FbKY1d3vjNybnhx_pLwJNEbvahZ0euHI7NUET5mPQahLZs_uZEeQuQkGU00T9RJ3XHLok/w640-h528/%E6%88%AA%E5%9C%96%202026-02-12%2017.13.06.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;之前有一台 Pi 3 閒置，查了一下記憶體有 1GB ，試試看吧！先把它更新到 Debian GNU/Linux 12 (bookworm) ，接著用 nvm 安裝出 node.js v22 環境，再調整 swap 到 1GB 來跑：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ uname -a&lt;/div&gt;&lt;div&gt;Linux raspberrypi 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr&amp;nbsp; 3 17:24:16 BST 2023 aarch64 GNU/Linux&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ lsb_release -a&lt;/div&gt;&lt;div&gt;No LSB modules are available.&lt;/div&gt;&lt;div&gt;Distributor ID:&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;Debian&lt;/div&gt;&lt;div&gt;Description:&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;Debian GNU/Linux 12 (bookworm)&lt;/div&gt;&lt;div&gt;Release:&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;12&lt;/div&gt;&lt;div&gt;Codename:&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;bookworm&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ apt policy raspberrypi-bootloader&lt;/div&gt;&lt;div&gt;raspberrypi-bootloader:&lt;/div&gt;&lt;div&gt;&amp;nbsp; Installed: 1:1.20230405-1&lt;/div&gt;&lt;div&gt;&amp;nbsp; Candidate: 1:1.20230405-1&lt;/div&gt;&lt;div&gt;&amp;nbsp; Version table:&lt;/div&gt;&lt;div&gt;&amp;nbsp;*** 1:1.20230405-1 500&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 500 http://archive.raspberrypi.org/debian bookworm/main arm64 Packages&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 100 /var/lib/dpkg/status&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ cat /etc/apt/sources.list.d/raspi.list&lt;/div&gt;&lt;div&gt;deb http://archive.raspberrypi.org/debian/ bookworm main&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ cat /etc/apt/sources.list&lt;/div&gt;&lt;div&gt;deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware&lt;/div&gt;&lt;div&gt;deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware&lt;/div&gt;&lt;div&gt;deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ lscpu&lt;/div&gt;&lt;div&gt;Architecture:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; aarch64&lt;/div&gt;&lt;div&gt;&amp;nbsp; CPU op-mode(s):&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 32-bit, 64-bit&lt;/div&gt;&lt;div&gt;&amp;nbsp; Byte Order:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Little Endian&lt;/div&gt;&lt;div&gt;CPU(s):&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 4&lt;/div&gt;&lt;div&gt;&amp;nbsp; On-line CPU(s) list:&amp;nbsp; &amp;nbsp;0-3&lt;/div&gt;&lt;div&gt;Vendor ID:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;ARM&lt;/div&gt;&lt;div&gt;&amp;nbsp; Model name:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Cortex-A53&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Model:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;4&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Thread(s) per core:&amp;nbsp; 1&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Core(s) per cluster: 4&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Socket(s):&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;-&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Cluster(s):&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Stepping:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; r0p4&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; CPU(s) scaling MHz:&amp;nbsp; 100%&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; CPU max MHz:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1200.0000&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; CPU min MHz:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;600.0000&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; BogoMIPS:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 38.40&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Flags:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;fp asimd evtstrm crc32 cpuid&lt;/div&gt;&lt;div&gt;Caches (sum of all):&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; L1d:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;128 KiB (4 instances)&lt;/div&gt;&lt;div&gt;&amp;nbsp; L1i:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;128 KiB (4 instances)&lt;/div&gt;&lt;div&gt;&amp;nbsp; L2:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 512 KiB (1 instance)&lt;/div&gt;&lt;div&gt;Vulnerabilities:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; Itlb multihit:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Not affected&lt;/div&gt;&lt;div&gt;&amp;nbsp; L1tf:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Not affected&lt;/div&gt;&lt;div&gt;&amp;nbsp; Mds:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Not affected&lt;/div&gt;&lt;div&gt;&amp;nbsp; Meltdown:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Not affected&lt;/div&gt;&lt;div&gt;&amp;nbsp; Mmio stale data:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Not affected&lt;/div&gt;&lt;div&gt;&amp;nbsp; Retbleed:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Not affected&lt;/div&gt;&lt;div&gt;&amp;nbsp; Spec store bypass:&amp;nbsp; &amp;nbsp; &amp;nbsp;Not affected&lt;/div&gt;&lt;div&gt;&amp;nbsp; Spectre v1:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Mitigation; __user pointer sanitization&lt;/div&gt;&lt;div&gt;&amp;nbsp; Spectre v2:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Not affected&lt;/div&gt;&lt;div&gt;&amp;nbsp; Srbds:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Not affected&lt;/div&gt;&lt;div&gt;&amp;nbsp; Tsx async abort:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Not affected&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ sudo vcgencmd measure_temp&lt;/div&gt;&lt;div&gt;temp=54.8'C&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ sudo vcgencmd measure_clock arm&lt;/div&gt;&lt;div&gt;frequency(48)=600062000&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ sudo vcgencmd get_mem gpu&lt;/div&gt;&lt;div&gt;gpu=76M&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ cat /proc/device-tree/model&lt;/div&gt;&lt;div&gt;Raspberry Pi 3 Model B Rev 1.2&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;$ cat /proc/cpuinfo | grep Model&lt;/div&gt;&lt;div&gt;Model&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;: Raspberry Pi 3 Model B Rev 1.2&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;---&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ openclaw onboard&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;lt;--- Last few GCs ---&amp;gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[17543:0x7fb04c0000]&amp;nbsp; &amp;nbsp;229321 ms: Scavenge 451.3 (463.5) -&amp;gt; 450.8 (464.3) MB, pooled: 0 MB, 21.16 / 0.00 ms&amp;nbsp; (average mu = 0.614, current mu = 0.204) allocation failure;&amp;nbsp;&lt;/div&gt;&lt;div&gt;[17543:0x7fb04c0000]&amp;nbsp; &amp;nbsp;229421 ms: Scavenge 452.0 (464.3) -&amp;gt; 451.5 (469.0) MB, pooled: 0 MB, 40.55 / 0.00 ms&amp;nbsp; (average mu = 0.614, current mu = 0.204) allocation failure;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;lt;--- JS stacktrace ---&amp;gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory&lt;/div&gt;&lt;div&gt;----- Native stack trace -----&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;1: 0xe13218 node::OOMErrorHandler(char const*, v8::OOMDetails const&amp;amp;) [openclaw]&lt;/div&gt;&lt;div&gt;&amp;nbsp;2: 0x11a7f8c v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, v8::OOMDetails const&amp;amp;) [openclaw]&lt;/div&gt;&lt;div&gt;&amp;nbsp;3: 0x11a813c v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&amp;amp;) [openclaw]&lt;/div&gt;&lt;div&gt;&amp;nbsp;4: 0x13ccadc&amp;nbsp; [openclaw]&lt;/div&gt;&lt;div&gt;&amp;nbsp;5: 0x13e4a50 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [openclaw]&lt;/div&gt;&lt;div&gt;&amp;nbsp;6: 0x13bb378 v8::internal::HeapAllocator::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [openclaw]&lt;/div&gt;&lt;div&gt;&amp;nbsp;7: 0x13bc1b0 v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [openclaw]&lt;/div&gt;&lt;div&gt;&amp;nbsp;8: 0x13972d0 v8::internal::Factory::NewFillerObject(int, v8::internal::AllocationAlignment, v8::internal::AllocationType, v8::internal::AllocationOrigin) [openclaw]&lt;/div&gt;&lt;div&gt;&amp;nbsp;9: 0x17b0b5c v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [openclaw]&lt;/div&gt;&lt;div&gt;10: 0x1cc19d4&amp;nbsp; [openclaw]&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ free -h&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;total&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; used&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; free&amp;nbsp; &amp;nbsp; &amp;nbsp; shared&amp;nbsp; buff/cache&amp;nbsp; &amp;nbsp;available&lt;/div&gt;&lt;div&gt;Mem:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;909Mi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;209Mi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;555Mi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;3.8Mi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;212Mi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;700Mi&lt;/div&gt;&lt;div&gt;Swap:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;99Mi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 99Mi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;160Ki&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ sudo dphys-swapfile swapoff&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ sudo vim /etc/dphys-swapfile&lt;/div&gt;&lt;div&gt;CONF_SWAPSIZE=1024&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ sudo dphys-swapfile setup&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ sudo dphys-swapfile swapon&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ free -h&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;total&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; used&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; free&amp;nbsp; &amp;nbsp; &amp;nbsp; shared&amp;nbsp; buff/cache&amp;nbsp; &amp;nbsp;available&lt;/div&gt;&lt;div&gt;Mem:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;909Mi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;313Mi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;428Mi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;5.0Mi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;236Mi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;596Mi&lt;/div&gt;&lt;div&gt;Swap:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1.0Gi&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 0B&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1.0Gi&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;@raspberrypi:~ $ NODE_OPTIONS="--max-old-space-size=768" openclaw onboard&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&#129438; OpenClaw 2026.2.9 (33c75cb) — I read logs so you can keep pretending you don't have to.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄&lt;/div&gt;&lt;div&gt;██░▄▄▄░██░▄▄░██░▄▄▄██░▀██░██░▄▄▀██░████░▄▄▀██░███░██&lt;/div&gt;&lt;div&gt;██░███░██░▀▀░██░▄▄▄██░█░█░██░█████░████░▀▀░██░█░█░██&lt;/div&gt;&lt;div&gt;██░▀▀▀░██░█████░▀▀▀██░██▄░██░▀▀▄██░▀▀░█░██░██▄▀▄▀▄██&lt;/div&gt;&lt;div&gt;▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#129438; OPENCLAW &#129438;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;┌&amp;nbsp; OpenClaw onboarding&lt;/div&gt;&lt;div&gt;│&lt;/div&gt;&lt;div&gt;◇&amp;nbsp; Security ──────────────────────────────────────────────────────────────────────────────╮&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; Security warning — please read.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; OpenClaw is a hobby project and still in beta. Expect sharp edges.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; This bot can read files and run actions if tools are enabled.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; A bad prompt can trick it into doing unsafe things.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; If you’re not comfortable with basic security and access control, don’t run OpenClaw.&amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; Ask someone experienced to help before enabling tools or exposing it to the internet.&amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; Recommended baseline:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; - Pairing/allowlists + mention gating.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; - Sandbox + least-privilege tools.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; - Keep secrets out of the agent’s reachable filesystem.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; - Use the strongest available model for any bot with tools or untrusted inboxes.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; Run regularly:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; openclaw security audit --deep&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; openclaw security audit --fix&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; Must read: https://docs.openclaw.ai/gateway/security&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;│&lt;/div&gt;&lt;div&gt;├─────────────────────────────────────────────────────────────────────────────────────────╯&lt;/div&gt;&lt;div&gt;│&lt;/div&gt;&lt;div&gt;◆&amp;nbsp; I understand this is powerful and inherently risky. Continue?&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ○ Yes / ● No&lt;/div&gt;&lt;div&gt;└&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;</description><link>http://blog.changyy.org/2026/02/linux-openclaw-raspberrypi-debian-12.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmzkzSADFVQvY4kzlK-nFF0jMTuYB6PjB2bhiJ_ttVVrmAvLMDPuzPLC4_RcDIdijxIGcVAyZjz9EEV5-HQDBE6c3JEDaUDMqjsKEJ8IEo7o6Oon2t24cKdr3FbKY1d3vjNybnhx_pLwJNEbvahZ0euHI7NUET5mPQahLZs_uZEeQuQkGU00T9RJ3XHLok/s72-w640-h528-c/%E6%88%AA%E5%9C%96%202026-02-12%2017.13.06.jpg" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-7703641559131790256</guid><pubDate>Thu, 01 Jan 2026 15:53:00 +0000</pubDate><atom:updated>2026-01-01T23:53:13.310+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">冗長碎碎唸</category><title>悠</title><description>&lt;p&gt;2026年，突然想用這個字了...&lt;/p&gt;&lt;p&gt;新年快樂&lt;/p&gt;</description><link>http://blog.changyy.org/2026/01/u.html</link><author>noreply@blogger.com (Unknown)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-9145683513407757652</guid><pubDate>Sun, 21 Sep 2025 14:32:00 +0000</pubDate><atom:updated>2025-09-21T22:32:58.910+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">冗長碎碎唸</category><title>重度使用 Claude Code 五個月後...</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8VHYrZ6p4dycP6tJx5vPfqo2c5w-zruLivYCYXjaee6hr08eQFiYYr8bKT3Axh-Nk0MylDNTLSnmmSg54nTQwlLMLyiOVCa0to70VJzTl5SWAet8AdbrrZVPBn91G4LwnG2bTOqbfRal0xhzNo9QH_aXVS2NSQ-Lv3ABPisw4VOUn0hqObdt4M5v7ahQW/s1786/Screenshot%202025-09-21%20at%2022.18.35.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1362" data-original-width="1786" height="488" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8VHYrZ6p4dycP6tJx5vPfqo2c5w-zruLivYCYXjaee6hr08eQFiYYr8bKT3Axh-Nk0MylDNTLSnmmSg54nTQwlLMLyiOVCa0to70VJzTl5SWAet8AdbrrZVPBn91G4LwnG2bTOqbfRal0xhzNo9QH_aXVS2NSQ-Lv3ABPisw4VOUn0hqObdt4M5v7ahQW/w640-h488/Screenshot%202025-09-21%20at%2022.18.35.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;累積到上個月，已經掏錢給 Claude 滿一年了，從一開始 claude.ai 的用法，不停地在瀏覽器上詢問，自己也開發了簡單的小工具，目的是把程式碼打包成單一檔案，方便超額後可以續作 XD 不過，這樣的生態慢慢在 2025.02 之後都改變了。我也從 vim 模式轉成 vscode + github copilot + agent mode ，然而，當我又摸熟 github copilot + agent mode&amp;nbsp;後，又轉進了 claude code 的世界，並在 2025.07 也提播助理費到百元美金 XD 好用，當然要給他加薪一下&lt;/p&gt;&lt;p&gt;如果要說最大的生活改變？可能就是進入到定義任務的開發模式了，以及享受慣老闆機制，凡事避開自己親自 coding ，自己不 coding 也能欣賞 AI coding，也是一種自我成長的過程，可以接納更多種的解方。&lt;/p&gt;&lt;p&gt;慣老闆方面也要體驗一下的，當然就想要極致壓榨 AI ，倒垃圾前、洗澡前、睡前下個指令是基本的，同時開 2-4 個 Terminal 做不同的事也是合情合理，只是到頭來自己的 context switch 反而吃重，並且隨著 AI 能力越來越強後，漸漸地收斂到同時做 1~2 件事就好，放過自己吧！&lt;/p&gt;&lt;p&gt;放過自己後，就是享受生活的開始，不再計較要怎樣操爆 AI ，反而是在生活時間的碎片化下，如何靠 AI 輔助串起一切，凡事盡可能規劃 Save/Load 架構，做事做到一個地步肯定要叫 AI 更新 CLAUDE.md 檔案，甚至另外規劃個 PLAN.md ，就像小老闆時時刻刻盯著 KPI 。那何謂享受？既然等待 AI 的時間慢慢拉長（AI處理的任務深度加深），就開始看一堆動畫 XD 想想倒看了不少：&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;膽大黨&lt;/li&gt;&lt;li&gt;阪本的日常&lt;/li&gt;&lt;li&gt;怪獸8號&lt;/li&gt;&lt;li&gt;薰香花朵凜然綻放&lt;/li&gt;&lt;li&gt;治癒魔法的錯誤使用法&lt;/li&gt;&lt;li&gt;我要架招一切&lt;/li&gt;&lt;li&gt;我獨自升級&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;現在是 2025-09-21 ，打算讓 Claude code 卸下重擔。開始試試 OpenAI codex ，從一開始對話窗上不能上傳圖片、不能工作時先打好下一道指令等等， codex 版本迭代後，基本上跟 Claude Code 的差距越來越小了。再加上 OpenAI GPT-5 的成效，其實很接近 Claude Sonnect 4 / Opus 4.1 ，只要任務定義的清楚，基本上都是一樣的。&lt;/p&gt;&lt;p&gt;我想，這大概就是另類的小確幸吧？AI 輔助十分競爭，各家業者的差距也在縮減。&lt;/p&gt;&lt;p&gt;不過，也有些殘忍的事正在發生，美國已經大量關閉新鮮人的職缺，原本認為中高階者會被新人+AI輔助取代，隨著時間已經變成新人的職缺大量縮減，因為有經驗者 + AI 輔助可以做更多的事，企業也不用等新人犯錯練等。就像十年前有錢的新創，起手式先掏錢挖走有經驗的人。&lt;/p&gt;&lt;p&gt;這件事在工作上也滿有感受到的，觀察到同事正進行一個尚未有經驗的任務時，原本期待 AI輔助的過程，可以讓同事朝向有效的路線發展，事實上還是很吃“提問者的提問方式“，因為 AI 也會順著提問者的思維去規劃，回歸到下 prompt 的品質依舊重要。觀察到一些計劃方向也出現繞路的情況，所幸，隨著實務經驗增加後，還是回歸了正途。這件事代表：雇用有經驗者 + AI 輔助，還是跟以往的生態一樣的，只是一個人的經驗價值，仍有可能因為 AI 的出現被貶低。&lt;/p&gt;&lt;p&gt;目前 開發者 與 AI 輔助仍努力的找個平衡點，應該不是 AI 的泡沫化？略知很多 AI 服務（如 Perplexity）仍是努力燒錢中，尚未損益平衡，這也是恰巧聽到矽谷 podcast ，行內在搞新創的都有時時追蹤業內的頭部，據說&amp;nbsp;Anthropic 話語權仍很好的，而整合類（沒有研發自己的 LLM且本身也沒錢研發）都非常辛苦，也怕搞 LLM 大廠推出新服務就打亂了自己的步調。&lt;/p&gt;&lt;p&gt;而最底層食物鏈的開發者呢？我想，真的只剩投資了 :P 只能配置好資產（大盤指數也行也夠），避免產業鏈一個瞬間轉身就...受到重傷 Orz&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJI9bb2g9bl2M2PaZeXKXzGfkswjoJW2eWC-FGERYCQiSn9W3Dgl1Kvb2SEegl616gz1OmFUaIWQIJlRi51gZosHlUnWdp-xZKbW-Wo1xOTbwm2XFMUnkc-a2PtI8nqvecYENCe6gQzZPotjridvegVvlhNgPslfbc0djSLROia4rc8H79MXpm2weABbtB/s2044/Screenshot%202025-09-21%20at%2022.10.24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1344" data-original-width="2044" height="420" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJI9bb2g9bl2M2PaZeXKXzGfkswjoJW2eWC-FGERYCQiSn9W3Dgl1Kvb2SEegl616gz1OmFUaIWQIJlRi51gZosHlUnWdp-xZKbW-Wo1xOTbwm2XFMUnkc-a2PtI8nqvecYENCe6gQzZPotjridvegVvlhNgPslfbc0djSLROia4rc8H79MXpm2weABbtB/w640-h420/Screenshot%202025-09-21%20at%2022.10.24.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description><link>http://blog.changyy.org/2025/09/claude-code.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8VHYrZ6p4dycP6tJx5vPfqo2c5w-zruLivYCYXjaee6hr08eQFiYYr8bKT3Axh-Nk0MylDNTLSnmmSg54nTQwlLMLyiOVCa0to70VJzTl5SWAet8AdbrrZVPBn91G4LwnG2bTOqbfRal0xhzNo9QH_aXVS2NSQ-Lv3ABPisw4VOUn0hqObdt4M5v7ahQW/s72-w640-h488-c/Screenshot%202025-09-21%20at%2022.18.35.png" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-8161671849072609150</guid><pubDate>Thu, 26 Jun 2025 13:57:00 +0000</pubDate><atom:updated>2025-06-26T22:44:13.149+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">dd</category><category domain="http://www.blogger.com/atom/ns#">diskutil</category><category domain="http://www.blogger.com/atom/ns#">macos</category><category domain="http://www.blogger.com/atom/ns#">microSD</category><category domain="http://www.blogger.com/atom/ns#">recovery</category><category domain="http://www.blogger.com/atom/ns#">兒童相機</category><category domain="http://www.blogger.com/atom/ns#">救援筆記</category><category domain="http://www.blogger.com/atom/ns#">記憶卡損毀</category><title>兒童相機記憶卡損毀救援筆記 - 在 macOS 修復摔壞的 microSD 卡資料</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhonUplreWeg7WPbVFZQn71Y9T38SLAiS7RyK7AeLLD0UcCXVEOpQJ6XDTJKf3_x3ny4krRefkimvt_ipBqKMifLUOW8RrvH_6kqE_iko_ioG3yNCTF1TCySn7NK94hzuC5s2f__ov7MWlGLotWEAmA374LZTGRLCz57qAQnnfgG_8if9jLL31QSBWsSp-r/s1902/microSD-help.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1388" data-original-width="1902" height="468" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhonUplreWeg7WPbVFZQn71Y9T38SLAiS7RyK7AeLLD0UcCXVEOpQJ6XDTJKf3_x3ny4krRefkimvt_ipBqKMifLUOW8RrvH_6kqE_iko_ioG3yNCTF1TCySn7NK94hzuC5s2f__ov7MWlGLotWEAmA374LZTGRLCz57qAQnnfgG_8if9jLL31QSBWsSp-r/w640-h468/microSD-help.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;兩三年前買了兒童相機給小孩亂拍，坊間大部分的兒童相機都大同小異，有的著重相機畫素，有的著重拍攝的方便性，像是可自拍、觸控螢幕等。後來，我選了觸控螢幕款，畢竟方便第一，至於畫素、閃光燈等等的就放掉，並買了一張 32GB microSD ，讓小孩大小可隨便錄影。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;最近出遊時，兒童相機在開機的狀態下，不小心摔到地上，接著無法拍照（儲存），也看不到所有已拍攝的資料。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;當下在想，這種 microSD 跟 SSD 的，不像以前的 HDD 有轉盤在那邊轉，應該不至於壞軌，再細想一下，有可能像 OS 優化連續錄影/開多檔快速寫檔案，但這時撞到地上導致 OS 狀態異常，沒有把資料收尾而損毀，可能導致 micorSD 檔案系統 Header 就被破壞掉，而不是 microSD 受到什麼物理性的破壞。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著，就來煩 AI 了 XD 我自己是先把損毀的 microSD 透過 macOS 內建的 Disk Utility 先備份一份 Image 檔案（備份過程也不是很正常的結束，但至少產生了一個 img 檔案），接著拿另一張同樣容量的 32GB microSD 來處理，避免一開始就對原始 microSD 卡操作。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;最後，透過跟 AI 聊天的過程，他認為我的想法/直覺有機會解掉，於是乎就操作看看，出發點是想重建 microSD header，流程：&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;先把損毀的 microSD卡(A)，透過 macOS 內建 Disk Utility 先備份一份 Image 檔案，產出 a_card.img&lt;/li&gt;&lt;li&gt;製作乾淨的 microSD卡(B)，把額外一張 microSD 卡放入兒童相機格式化，確保資料檔案系統跟損毀的 microSD 卡一樣，在產出 clean_b_card.img&lt;/li&gt;&lt;li&gt;後續則是在 microSD卡(B) 操作，用 a_card.img 資料覆蓋 microSD卡(B)，這時 microSD卡(B) 插入兒童相機一樣會顯示檔案損毀的狀態&lt;/li&gt;&lt;li&gt;最後則是拿 clean_b_card.img 部分資料去覆蓋 microSD卡(B) ，直到可以 OS 正常讀出資料&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;執行過程中，AI 其實會去猜檔案系統為何，然後摸索 Header 大小，像是建議一開始用 8MB 來猜，然後測試完看不到資料，又說可以試試 4MB （一樣看不到資料），又說要不要試試 2MB，這時我反而去問他應該要從 header size 小到大來測試，也讓 AI 想起可以用 macos 內建的 diskutil 指令查詢資料，此例是 microSD 插入後被系統安置在 /dev/disk10 ，相關指令如：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;%&amp;nbsp; diskutil info /dev/disk10 | grep -E "(Block|Cluster|Sector)"&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;Device Block Size:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;512 Bytes&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;Allocation Block Size:&amp;nbsp; &amp;nbsp; &amp;nbsp;512 Bytes&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;不然根據 AI 的建議，他的操作每一次都要重新製作一次 microSD卡(B) ，滿費時的，最終我就只測試三次，分別是 8MB, 4MB ，接著到 512KB 就搞定了（過程重建 3次資料），而我請 AI 由小到大的 header size 測試，他給予的指令流程：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;# 1. 先一次性寫入 A 卡資料（只做這一次）&lt;/div&gt;&lt;div&gt;diskutil unmountDisk /dev/disk10&lt;/div&gt;&lt;div&gt;sudo dd if=a_card.img of=/dev/rdisk10 bs=1m&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 2. 從最小範圍開始測試 - 只覆蓋 Boot Sector (512 bytes)&lt;/div&gt;&lt;div&gt;sudo dd if=clean_b_card.img of=/dev/rdisk10 bs=512 count=1 conv=notrunc&lt;/div&gt;&lt;div&gt;diskutil mountDisk /dev/disk10&lt;/div&gt;&lt;div&gt;# 檢查是否有資料，如果沒有繼續下一步&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 3. 如果 512 bytes 不夠，擴大到 40KB（到 FAT 表開始位置）&lt;/div&gt;&lt;div&gt;diskutil unmountDisk /dev/disk10&lt;/div&gt;&lt;div&gt;sudo dd if=clean_b_card.img of=/dev/rdisk10 bs=1k count=40 conv=notrunc&lt;/div&gt;&lt;div&gt;diskutil mountDisk /dev/disk10&lt;/div&gt;&lt;div&gt;# 檢查結果&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 4. 如果還不夠，擴大到 100KB&lt;/div&gt;&lt;div&gt;diskutil unmountDisk /dev/disk10&lt;/div&gt;&lt;div&gt;sudo dd if=clean_b_card.img of=/dev/rdisk10 bs=1k count=100 conv=notrunc&lt;/div&gt;&lt;div&gt;diskutil mountDisk /dev/disk10&lt;/div&gt;&lt;div&gt;# 檢查結果&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 5. 繼續擴大到 500KB&lt;/div&gt;&lt;div&gt;diskutil unmountDisk /dev/disk10&lt;/div&gt;&lt;div&gt;sudo dd if=clean_b_card.img of=/dev/rdisk10 bs=1k count=500 conv=notrunc&lt;/div&gt;&lt;div&gt;diskutil mountDisk /dev/disk10&lt;/div&gt;&lt;div&gt;# 檢查結果&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 6. 最後到 1MB&lt;/div&gt;&lt;div&gt;diskutil unmountDisk /dev/disk10&lt;/div&gt;&lt;div&gt;sudo dd if=clean_b_card.img of=/dev/rdisk10 bs=1k count=1024 conv=notrunc&lt;/div&gt;&lt;div&gt;diskutil mountDisk /dev/disk10&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;我後來只做這些，就看得到資料：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% diskutil unmountDisk /dev/disk10&lt;/div&gt;&lt;div&gt;% sudo dd if=a_card.img of=/dev/rdisk10 bs=1m&lt;/div&gt;&lt;div&gt;9297+1 records in&lt;/div&gt;&lt;div&gt;9297+1 records out&lt;/div&gt;&lt;div&gt;9748611584 bytes transferred in 642.300766 secs (15177643 bytes/sec)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;% sudo dd if=clean_b_card.img of=/dev/rdisk10 bs=512 count=1 conv=notrunc&lt;/div&gt;&lt;div&gt;Password:&lt;/div&gt;&lt;div&gt;1+0 records in&lt;/div&gt;&lt;div&gt;1+0 records out&lt;/div&gt;&lt;div&gt;512 bytes transferred in 0.009456 secs (54146 bytes/sec)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;% diskutil mountDisk /dev/disk10&lt;/div&gt;&lt;div&gt;Volume(s) mounted successfully&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如此能看到資料後，也確定有些檔案相機的確沒正確寫入，顯示損毀，猜測應該是晚一點寫檔的機制(cache)，導致有些檔案並未正確收尾。&lt;br /&gt;&lt;br /&gt;最後在靠這些：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;&lt;div&gt;% diskutil unmountDisk /dev/disk10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Unmount of all volumes on disk10 was successful&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;% sudo fsck_msdos -f -y /dev/disk10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Password:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;** /dev/rdisk10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;** Phase 1 - Preparing FAT&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;** Phase 2 - Checking Directories&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;** Phase 3 - Checking for Orphan Clusters&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Warning: 175 files, 31120736 KiB free (972523 clusters)&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;% brew install testdisk&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;% sudo photorec /dev/disk10&lt;/div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;p&gt;過程：&lt;/p&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;PhotoRec 7.2, Data Recovery Utility, February 2024&lt;/div&gt;&lt;div&gt;Christophe GRENIER &amp;lt;grenier@cgsecurity.org&amp;gt;&lt;/div&gt;&lt;div&gt;https://www.cgsecurity.org&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Disk /dev/disk10 - 31 GB / 29 GiB (RO)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;Partition&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Start&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; End&amp;nbsp; &amp;nbsp; Size in sectors&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;P FAT32&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 0&amp;nbsp; &amp;nbsp;62333951&amp;nbsp; &amp;nbsp;62333952 [NO NAME]&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Destination /path/recup_dir&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Pass 1 - Reading sector&amp;nbsp; &amp;nbsp; 6458368/62333952, 217 files found&lt;/div&gt;&lt;div&gt;Elapsed time 0h02m02s - Estimated time to completion 0h17m35&lt;/div&gt;&lt;div&gt;jpg: 101 recovered&lt;/div&gt;&lt;div&gt;txt: 94 recovered&lt;/div&gt;&lt;div&gt;mov: 17 recovered&lt;/div&gt;&lt;div&gt;gz: 5 recovered&lt;/div&gt;&lt;/blockquote&gt;&lt;p&gt;希望可以救多一點&amp;nbsp;&lt;/p&gt;</description><link>http://blog.changyy.org/2025/06/microsd-data-recovery-macos-dd-tutorial.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhonUplreWeg7WPbVFZQn71Y9T38SLAiS7RyK7AeLLD0UcCXVEOpQJ6XDTJKf3_x3ny4krRefkimvt_ipBqKMifLUOW8RrvH_6kqE_iko_ioG3yNCTF1TCySn7NK94hzuC5s2f__ov7MWlGLotWEAmA374LZTGRLCz57qAQnnfgG_8if9jLL31QSBWsSp-r/s72-w640-h468-c/microSD-help.png" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-3049550607750521848</guid><pubDate>Tue, 22 Apr 2025 14:30:00 +0000</pubDate><atom:updated>2025-04-22T22:30:00.117+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cgi</category><category domain="http://www.blogger.com/atom/ns#">development</category><category domain="http://www.blogger.com/atom/ns#">embedded linux</category><category domain="http://www.blogger.com/atom/ns#">html</category><category domain="http://www.blogger.com/atom/ns#">http.server</category><category domain="http://www.blogger.com/atom/ns#">proxy</category><category domain="http://www.blogger.com/atom/ns#">python3</category><category domain="http://www.blogger.com/atom/ns#">request</category><title>Python 開發筆記 - 使用 http.server 建置嵌入式產品網頁開發環境與 Proxy 機制 </title><description>&lt;div&gt;這個需求快十年前弄過，那時是用 webpack proxy server，可以在習慣的桌機開發網頁，接著對於 CGI 查詢就透過 proxy 導向到 device CGI&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;然後，現在弄一個 Python ，方便後續在老 server 上運行，畢竟肥肥的程式碼搭配檔案系統大小寫問題，還是交給 server 檔案系統處理吧，如此，未來有部分 html/js/css code 要快速測試，就只需要到指定機器上，運行&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;$ python3 proxy-server.py --port 12345 --proxy-target 'http://192.168.123.234:80' --proxy-paths '/cgi-bin/' -d project/path/html/code/&lt;/div&gt;&lt;div&gt;Serving files from directory: project/path/html/code/&lt;/div&gt;&lt;div&gt;Serving HTTP on 0.0.0.0 port 12345 (multi-threaded) ...&lt;/div&gt;&lt;div&gt;Proxying paths ['/cgi-bin/'] to http://192.168.123.234:80&lt;/div&gt;&lt;div&gt;^C&lt;/div&gt;&lt;div&gt;Server stopped.&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如此對於 CGI request 就會自動導向到 'http://192.168.123.234:80'&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;程式碼純筆記，這都是 AI 產的：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;#!/usr/bin/env python3&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;import http.server&lt;/div&gt;&lt;div&gt;import socketserver&lt;/div&gt;&lt;div&gt;import http.client&lt;/div&gt;&lt;div&gt;import argparse&lt;/div&gt;&lt;div&gt;import os&lt;/div&gt;&lt;div&gt;import sys&lt;/div&gt;&lt;div&gt;import socket&lt;/div&gt;&lt;div&gt;from urllib.parse import urlparse&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 使用 ThreadingMixIn 來處理並發請求&lt;/div&gt;&lt;div&gt;class ThreadedHTTPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; daemon_threads = True&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; allow_reuse_address = True&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;class ProxyHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 代理目標地址&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; proxy_target = None&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 需要代理的路徑前綴&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; proxy_paths = []&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 連接池（簡單實現）&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; conn_pool = {}&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; def __init__(self, *args, **kwargs):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 確保可以正確處理目錄參數&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; super().__init__(*args, **kwargs)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; def do_GET(self):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 檢查是否需要代理這個請求&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for path_prefix in self.proxy_paths:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if self.path.startswith(path_prefix):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.proxy_request()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 如果不需要代理，則使用默認處理方式&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; super().do_GET()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; def do_POST(self):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 為 POST 請求也提供代理功能&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for path_prefix in self.proxy_paths:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if self.path.startswith(path_prefix):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.proxy_request()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; super().do_POST()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; def get_connection(self, host, is_ssl=False):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; """從連接池獲取連接，或建立新連接"""&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; key = (host, is_ssl)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if key not in self.conn_pool:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if is_ssl:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.conn_pool[key] = http.client.HTTPSConnection(host)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.conn_pool[key] = http.client.HTTPConnection(host)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 檢查連接是否仍然有效&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; conn = self.conn_pool[key]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 嘗試使用一個非阻塞的方式檢查連接狀態&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; old_timeout = conn.sock.gettimeout()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; conn.sock.settimeout(0.01)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; conn.sock.recv(1, socket.MSG_PEEK)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; conn.sock.settimeout(old_timeout)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; except (socket.error, AttributeError):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 連接已關閉或存在問題，創建新連接&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if is_ssl:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.conn_pool[key] = http.client.HTTPSConnection(host)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.conn_pool[key] = http.client.HTTPConnection(host)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; except Exception:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 其他類型的錯誤，可能連接仍然有效&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; pass&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return self.conn_pool[key]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; def proxy_request(self):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; """使用 http.client 處理代理請求，這在 Python 3.2 中更高效"""&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; target_url = urlparse(self.proxy_target + self.path)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 獲取請求頭部&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; headers = {}&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for key, value in self.headers.items():&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 排除一些特定的頭部&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if key.lower() not in ('host', 'content-length'):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; headers[key] = value&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 設置正確的 Host 頭部&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; headers['Host'] = target_url.netloc&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 讀取請求體（如果有）&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; content_length = int(self.headers.get('Content-Length', 0))&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; body = self.rfile.read(content_length) if content_length &amp;gt; 0 else None&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 確定是否使用 HTTPS&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; is_ssl = target_url.scheme == 'https'&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 獲取或創建連接&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; conn = self.get_connection(target_url.netloc, is_ssl)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 構建請求路徑&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; request_path = target_url.path&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if target_url.query:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; request_path += '?' + target_url.query&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 發送請求&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; conn.request(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; method=self.command,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; url=request_path,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; body=body,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; headers=headers&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 獲取響應&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; response = conn.getresponse()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 設置響應狀態碼&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.send_response(response.status)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 設置響應頭部&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for header in response.getheaders():&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; key, value = header&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if key.lower() != 'transfer-encoding':&amp;nbsp; # 排除特定頭部&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.send_header(key, value)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.end_headers()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 發送響應體&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.wfile.write(response.read())&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 不關閉連接，將其保留在連接池中&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; except http.client.HTTPException as e:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 處理 HTTP 錯誤&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.send_response(500)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.send_header('Content-Type', 'text/plain; charset=utf-8')&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.end_headers()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.wfile.write("HTTP Error: {}".format(str(e)).encode('utf-8'))&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; except Exception as e:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 處理其他錯誤&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.send_response(500)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.send_header('Content-Type', 'text/plain; charset=utf-8')&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.end_headers()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.wfile.write("Proxy error: {}".format(str(e)).encode('utf-8'))&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def run_server(port=8000, proxy_target="http://localhost:8080", proxy_paths=["/cgi-bin/"], directory=None):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 設置代理目標和路徑&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; ProxyHTTPRequestHandler.proxy_target = proxy_target&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; ProxyHTTPRequestHandler.proxy_paths = proxy_paths&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 確保代理目標是有效的 URL&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if not ProxyHTTPRequestHandler.proxy_target.startswith(('http://', 'https://')):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ProxyHTTPRequestHandler.proxy_target = "http://{}".format(ProxyHTTPRequestHandler.proxy_target)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 移除代理目標末尾的斜線（如果有）&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if ProxyHTTPRequestHandler.proxy_target.endswith('/'):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ProxyHTTPRequestHandler.proxy_target = ProxyHTTPRequestHandler.proxy_target[:-1]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 設置工作目錄&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if directory:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; os.chdir(directory)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print("Serving files from directory: {}".format(directory))&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; else:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print("Serving files from current directory: {}".format(os.getcwd()))&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 提高 socket 超時時間以處理慢速連接&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; socket.setdefaulttimeout(60)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 建立服務器（使用線程化服務器）&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; server_address = ("", port)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; httpd = ThreadedHTTPServer(server_address, ProxyHTTPRequestHandler)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print("Serving HTTP on 0.0.0.0 port {} (multi-threaded) ...".format(port))&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print("Proxying paths {} to {}".format(proxy_paths, proxy_target))&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; httpd.serve_forever()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; except KeyboardInterrupt:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print("\nServer stopped.")&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; httpd.server_close()&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;if __name__ == "__main__":&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 解析命令行參數&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; parser = argparse.ArgumentParser(description='HTTP Server with proxy capabilities (optimized for Python 3.2)')&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; parser.add_argument('--port', type=int, default=8000, help='Port to listen on (default: 8000)')&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; parser.add_argument('--proxy-target', type=str, default="http://localhost:8080",&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; help='Target server to proxy to (default: http://localhost:8080)')&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; parser.add_argument('--proxy-paths', type=str, default="/cgi-bin/",&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; help='Comma-separated list of path prefixes to proxy (default: /cgi-bin/)')&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; parser.add_argument('--directory', '-d', type=str, default=None,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; help='Specify directory to serve files from (default: current directory)')&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; args = parser.parse_args()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; proxy_paths = [path.strip() for path in args.proxy_paths.split(',')]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; run_server(args.port, args.proxy_target, proxy_paths, args.directory)&lt;/div&gt;&lt;/blockquote&gt;</description><link>http://blog.changyy.org/2025/04/python-http-server-embedded-device-proxy-cgi-request.html</link><author>noreply@blogger.com (Unknown)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-3568186015761778540</guid><pubDate>Thu, 17 Apr 2025 13:30:00 +0000</pubDate><atom:updated>2025-04-17T21:30:00.117+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">agent</category><category domain="http://www.blogger.com/atom/ns#">Android</category><category domain="http://www.blogger.com/atom/ns#">autorun</category><category domain="http://www.blogger.com/atom/ns#">disable</category><category domain="http://www.blogger.com/atom/ns#">file transfer</category><category domain="http://www.blogger.com/atom/ns#">phone</category><title>[macOS] 關閉 Android File Transfer Agent 自動啟動 @ MacBook Air M2 / macOS 15.4.1</title><description>&lt;div&gt;雖然 Android File Transfer 差不多被官方捨棄掉了，但我有舊版 Android 開發機還是很依賴它。目前官方不能下載，想下載可參考這篇：&lt;a href="https://blog.changyy.org/2024/07/macos-AndroidFileTransfer-dmg-download-url.html" rel="nofollow" target="_blank"&gt;macOS - android fileTransfer 下載位置 AndroidFileTransfer.dmg&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這篇主要是要記錄關閉 Android File Transfer Agent 的方式，以往都是自己跑去系統位置去稍微破壞一下檔案名稱路徑等等的架構，這次就多了解一下系統架構：&lt;/div&gt;&lt;div&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;系統 -&amp;gt; 一般 -&amp;gt; 登入項目與延伸功能 -&amp;gt; 在登入時打開，在此處進行關閉&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPMVYv-ARIHNiRiQfH7zBEYOxhzyjHHioI5rc3UiHWM-zTQh6lkE2Gr01dcfdMBDPW3IYLct55BHijRNUh9SrmVTUL0oKX49aICwskSf5sNLS4zQ3qsJ8sDyTcmhCF3T4iJr4OgB5yF-7DgKI6gKnoEIA0K_psddswjwGHyxXGQGjeSfeYE990JXVxw8aP/s1418/01.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="458" data-original-width="1418" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPMVYv-ARIHNiRiQfH7zBEYOxhzyjHHioI5rc3UiHWM-zTQh6lkE2Gr01dcfdMBDPW3IYLct55BHijRNUh9SrmVTUL0oKX49aICwskSf5sNLS4zQ3qsJ8sDyTcmhCF3T4iJr4OgB5yF-7DgKI6gKnoEIA0K_psddswjwGHyxXGQGjeSfeYE990JXVxw8aP/w640-h206/01.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;把 Android File Transfer Agent 所在處移除&lt;/li&gt;&lt;ul&gt;&lt;li&gt;~/Library/Application Support/Google/Android File Transfer/Android File Transfer Agent.app&lt;/li&gt;&lt;li&gt;/Applications/Android File Transfer.app/Contents/Helpers/Android File Transfer Agent.app&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLdbh2vFEzrURQL-1RzAcPRG4E92rH_sFIKAldcS3Js_Q35mTvMOX9DPze-v3kZj-WUL9wIUQ2ySK2tGddOgwcI4CL1XZCaFej830Uv1ozqYO5WTl4gowxHwVsr_UwuwzXkIiUe1_IoCSBhiDSJ0uzPSxQiDE5pnJcZr339h60hYSgDhykEerXLuX04e0E/s2372/02.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="860" data-original-width="2372" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLdbh2vFEzrURQL-1RzAcPRG4E92rH_sFIKAldcS3Js_Q35mTvMOX9DPze-v3kZj-WUL9wIUQ2ySK2tGddOgwcI4CL1XZCaFej830Uv1ozqYO5WTl4gowxHwVsr_UwuwzXkIiUe1_IoCSBhiDSJ0uzPSxQiDE5pnJcZr339h60hYSgDhykEerXLuX04e0E/w640-h232/02.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;li&gt;如此在應用程式開啟時，就不會重新再次註冊到 "在登入時打開"&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA4cxXy03-z6OddN98n4gr1efqijC2oRvtZzI7977kDiKfplYMGYAWQCqzySHpPIL-LRKiuEeItvd0PwrM5tE2TRS0Gn0U-gGxXW4j_0J0nJwfEr5yZZ2M4GctNVjlColg-kGGw4UObEk6Mt_1mMYB0UlEUC21VtEGrXvDZ13sRrwrXfdEGrN-Px_b_k42/s1046/03.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="810" data-original-width="1046" height="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA4cxXy03-z6OddN98n4gr1efqijC2oRvtZzI7977kDiKfplYMGYAWQCqzySHpPIL-LRKiuEeItvd0PwrM5tE2TRS0Gn0U-gGxXW4j_0J0nJwfEr5yZZ2M4GctNVjlColg-kGGw4UObEk6Mt_1mMYB0UlEUC21VtEGrXvDZ13sRrwrXfdEGrN-Px_b_k42/w640-h496/03.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;這樣可以避免 android phone 插上連接線時，不斷觸發煩人的彈跳視窗（手機或筆電都有），直到需要時，自己再去應用程式開啟 Android File Transfer 就夠了&lt;/div&gt;</description><link>http://blog.changyy.org/2025/04/macos-android-file-transfer-agent.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPMVYv-ARIHNiRiQfH7zBEYOxhzyjHHioI5rc3UiHWM-zTQh6lkE2Gr01dcfdMBDPW3IYLct55BHijRNUh9SrmVTUL0oKX49aICwskSf5sNLS4zQ3qsJ8sDyTcmhCF3T4iJr4OgB5yF-7DgKI6gKnoEIA0K_psddswjwGHyxXGQGjeSfeYE990JXVxw8aP/s72-w640-h206-c/01.jpg" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-6715621338234393166</guid><pubDate>Fri, 11 Apr 2025 13:22:00 +0000</pubDate><atom:updated>2025-04-11T21:22:32.296+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">MAC</category><category domain="http://www.blogger.com/atom/ns#">macos</category><category domain="http://www.blogger.com/atom/ns#">quality</category><category domain="http://www.blogger.com/atom/ns#">reduce</category><category domain="http://www.blogger.com/atom/ns#">screenshot</category><category domain="http://www.blogger.com/atom/ns#">檔案大小</category><category domain="http://www.blogger.com/atom/ns#">畫質</category><title>[macOS] 調整 MacBook screenshot 的畫質，從 png 調整成 jpg 縮小檔案大小 @ MacBook Air 15吋 / Apple M2</title><description>&lt;div&gt;關於 MacBook Print Screen 的畫質太高這件事，其實困擾我很久了，以往都是 screenshot 後，自己縮一下圖再傳出去，一週做個不到五次。但如果和同事工作交流更加頻繁時，會懶得縮圖，使得 Mail/Line/Stack 傳的截圖傳遞檔案很大，觀看者也要花更多的時間處理&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;隨口問問 AI ，可以靠指令調整，像是把預設儲存方式改成 JPG 並調整品質，這樣就搞定了！以 MacBook Air M2 15吋為例，預設整個畫面 screenshot 是落在快 5MB 大小（跟畫面內容複雜度有關），調整後約 1.5MB&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;我自己就弄成兩個 script 方便開關調整：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% cat reduce-macos-screenshot-quality.sh&lt;/div&gt;&lt;div&gt;#!/bin/bash&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;defaults read com.apple.screencapture&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;defaults write com.apple.screencapture type jpg&lt;/div&gt;&lt;div&gt;killall SystemUIServer&lt;/div&gt;&lt;div&gt;defaults write com.apple.screencapture jpg-quality 60&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;defaults read com.apple.screencapture&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% cat reset-macos-screenshot-quality.sh&amp;nbsp;&lt;/div&gt;&lt;div&gt;#!/bin/bash&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;defaults read com.apple.screencapture&lt;/div&gt;&lt;div&gt;#defaults write com.apple.screencapture type png&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;defaults delete com.apple.screencapture&lt;/div&gt;&lt;div&gt;killall SystemUIServer&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;defaults read com.apple.screencapture&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;預設的畫質：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;&lt;div&gt;% file raw.png&amp;nbsp;&lt;/div&gt;&lt;div&gt;~/User/Desktop/raw.png: PNG image data, 3420 x 2214, 8-bit/color RGBA, non-interlaced&lt;/div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;p&gt;調整後：&amp;nbsp;&lt;/p&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;&lt;div&gt;% file reduce.jpg&amp;nbsp;&lt;/div&gt;&lt;div&gt;~/User/Desktop/reduce.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 144x144, segment length 16, Exif Standard: [TIFF image data, big-endian, direntries=4, xresolution=62, yresolution=70, resolutionunit=2], baseline, precision 8, 3420x2214, components 3&lt;/div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;&lt;div&gt;% defaults read com.apple.screencapture&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;{&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "jpg-quality" = 60;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; type = jpg;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;收工&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;其他資訊：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;&lt;a href="https://support.apple.com/en-us/102646" rel="nofollow" target="_blank"&gt;Take a screenshot on Mac&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</description><link>http://blog.changyy.org/2025/04/macos-macbook-screenshot-reduce-quality-from-png-to-jpg.html</link><author>noreply@blogger.com (Unknown)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-2407871563318210661</guid><pubDate>Fri, 07 Mar 2025 13:32:00 +0000</pubDate><atom:updated>2025-03-07T21:32:00.115+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">docker</category><category domain="http://www.blogger.com/atom/ns#">google</category><category domain="http://www.blogger.com/atom/ns#">immich</category><category domain="http://www.blogger.com/atom/ns#">link</category><category domain="http://www.blogger.com/atom/ns#">nas</category><category domain="http://www.blogger.com/atom/ns#">photo</category><category domain="http://www.blogger.com/atom/ns#">setup</category><category domain="http://www.blogger.com/atom/ns#">symbolic</category><category domain="http://www.blogger.com/atom/ns#">Synology</category><category domain="http://www.blogger.com/atom/ns#">volumes</category><category domain="http://www.blogger.com/atom/ns#">yaml</category><title>Docker 開發筆記 - 在 Synology NAS 運行 immich 照片服務與 Symbolic link 管理方式 @ Synology DS723+</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpMsbvIZ3L-PQXSrvnsip-74ZhJIf5-MkLX0B-WUjkjUu4NS_gSAXKE5oTNRNmYEDfdTHjYiWbwAOAjyyMII7tnNGOVKrkty74tdcFZf8O2DxBiBvCz1ifFD6lHq4ows-qWscaZN5X0V2WbixybJANtMtP4K0XdRdSir1Qf5pvrBIB5K503SH6v5td6EOg/s2256/synology-docker-immich-00.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1104" data-original-width="2256" height="314" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpMsbvIZ3L-PQXSrvnsip-74ZhJIf5-MkLX0B-WUjkjUu4NS_gSAXKE5oTNRNmYEDfdTHjYiWbwAOAjyyMII7tnNGOVKrkty74tdcFZf8O2DxBiBvCz1ifFD6lHq4ows-qWscaZN5X0V2WbixybJANtMtP4K0XdRdSir1Qf5pvrBIB5K503SH6v5td6EOg/w640-h314/synology-docker-immich-00.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;周邊有強者好友真不錯，時常分享把一堆服務都搞自建方案，目的不是省錢，而是追求資料握在自己手中的架構。挑選 immich 大概算是公認做得不錯的相簿管理服務，其介面跟 Google Photos 很像，且支援人臉辨識也有依照地圖(OpenStreetMap)顯示照片拍攝位置&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX6a4JHRjbV_f2zpWMGoxKbVrLWEh0PvoBfsODuhxHBuEfP1peAzamyR5Aeln30JKnNcOcbfx0A4BmcPbeqQAxteyhIpZWZP3WH9H32bXBuTDtzwJycVLGMb0ZTfi3Djr_0O8Vbh00Rad4iS8pqpholu6VMWcc7SdRxSzUzhc-vtd0DMAtGvjOC89Bz-3R/s2008/synology-docker-immich-07.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1274" data-original-width="2008" height="406" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX6a4JHRjbV_f2zpWMGoxKbVrLWEh0PvoBfsODuhxHBuEfP1peAzamyR5Aeln30JKnNcOcbfx0A4BmcPbeqQAxteyhIpZWZP3WH9H32bXBuTDtzwJycVLGMb0ZTfi3Djr_0O8Vbh00Rad4iS8pqpholu6VMWcc7SdRxSzUzhc-vtd0DMAtGvjOC89Bz-3R/w640-h406/synology-docker-immich-07.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;而 immich 官方文件有非常方便架設 Docker 的筆記：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;&lt;a href="https://immich.app/docs/install/synology/" rel="nofollow" target="_blank"&gt;https://immich.app/docs/install/synology/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;在 Synology NAS 上，若有支援 Docker 的，可以在 Container Manager 中新增專案，這時要上傳 docker-composer.yml 檔案，則是在剛剛的教學網站有顯示：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;&lt;a href="https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml" rel="nofollow" target="_blank"&gt;https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/immich-app/immich/releases/latest/download/example.env" rel="nofollow" target="_blank"&gt;https://github.com/immich-app/immich/releases/latest/download/example.env&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;在此先決定了 NAS 上的檔案位置，把 Docker 運行所需的環境擺在 /docker/immich-app 目錄(這是 File Station 看到的路徑)，他實際的路徑位置會是 /volume1/docker/immich-app 。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgOLBiRLUr8BikyIy6IlE0t5LB2MHehfSO4Daoji50x2f11q1YfR9OLyJJPfjIK-sbiDLVYkAZMkpk9V3pbRJfbZYwTboAqCFWNIDrfI5rIeB9XEtGMcZ2-Xm5kUxsLuGY_Ca-nM3K5qvx9soZcFrm0GGfoaL-ZkFf7uSJpGCyBdBydHLARTmdCyI9yP8G/s2370/synology-docker-immich-01.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1276" data-original-width="2370" height="344" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgOLBiRLUr8BikyIy6IlE0t5LB2MHehfSO4Daoji50x2f11q1YfR9OLyJJPfjIK-sbiDLVYkAZMkpk9V3pbRJfbZYwTboAqCFWNIDrfI5rIeB9XEtGMcZ2-Xm5kUxsLuGY_Ca-nM3K5qvx9soZcFrm0GGfoaL-ZkFf7uSJpGCyBdBydHLARTmdCyI9yP8G/w640-h344/synology-docker-immich-01.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;這時 docker-compose.yml 可透過 Container Manager 上傳好，而 example.env 則是透過 File Station 拖拉進去改名成 .env&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6p3QFYGrb_SwmEzwad0CvJntRjlBBkIUBJg9crOBCe8TQx-MHMFEfbt3uH8KHCoWkTkGoMhAggUeBc0HKTTjALZF9emURP8kesYP7DlO7dDDQ-sZrDjm4kFweWfc1ZaBeQDmqHE-fievVWxpT6rSb5saIT950PgH6j3IOMcQGmpGGLLZjx4jKFe8gcnU9/s1870/synology-docker-immich-02.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="626" data-original-width="1870" height="214" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6p3QFYGrb_SwmEzwad0CvJntRjlBBkIUBJg9crOBCe8TQx-MHMFEfbt3uH8KHCoWkTkGoMhAggUeBc0HKTTjALZF9emURP8kesYP7DlO7dDDQ-sZrDjm4kFweWfc1ZaBeQDmqHE-fievVWxpT6rSb5saIT950PgH6j3IOMcQGmpGGLLZjx4jKFe8gcnU9/w640-h214/synology-docker-immich-02.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;這時直接在 Container Manager 運行時，應當會踩到問題，因為 library 或 postgres 目錄不存在，這時也繼續靠 File Station 建立，後續 Container Manager 運行就會正常了，正常到最後可以用 NAS_IP:2283 瀏覽起來，可以看到註冊畫面等。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這邊有幾個議題記錄一下：&lt;/div&gt;&lt;div&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;使用 NAS 反向代理伺服器，提供 https 連線 immich 服務&lt;/li&gt;&lt;li&gt;在 immich 使用外部圖庫 (External Library) ，將原本的 Synology Photos 匯入&lt;/li&gt;&lt;li&gt;在 immich External Library 如何使用 Symbolic link 來管理&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;首先，NAS 反向代理伺服器的設定還滿簡單的，找一個 port 來服務 https 連線，例如 2284 ，直接把它導向到 2283 ，這樣就搞定收工，未來就有 https://NAS_IP:2284 可用了！&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKPn0Pu-s9eBAmECUhXfFC5dE6Jr7gUq9jxAJgcDSOkUtboJ4Z-bj7IOnTxGIZ6n8aWtucerfgAyyWGNCAyBKUMzu621KDROTqyWDSfo0r04kHABX6V-DjGdAoVJ5iJY8rQaDfVl4vZNoXfwtHCYJLRWZKnRXFqUCGtAXZM33z5fcy-aU6nCEJ4B1Lo7Qr/s2054/synology-docker-immich-03.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1230" data-original-width="2054" height="384" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKPn0Pu-s9eBAmECUhXfFC5dE6Jr7gUq9jxAJgcDSOkUtboJ4Z-bj7IOnTxGIZ6n8aWtucerfgAyyWGNCAyBKUMzu621KDROTqyWDSfo0r04kHABX6V-DjGdAoVJ5iJY8rQaDfVl4vZNoXfwtHCYJLRWZKnRXFqUCGtAXZM33z5fcy-aU6nCEJ4B1Lo7Qr/w640-h384/synology-docker-immich-03.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著，使用外部圖庫部分，這邊跟 Docker 設定有關，必須把額外的資料像掛載進來，例如&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; volumes:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - ${UPLOAD_LOCATION}:/usr/src/app/upload&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - /etc/localtime:/etc/localtime:ro&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - &lt;b&gt;&lt;span style="color: red;"&gt;/var/services/homes/UserID/Photos:/synology-photo&lt;/span&gt;&lt;/b&gt;:ro&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;透過把 Synology 在使用者家目錄的位置掛進來，這樣對 immich docker 環境中，就多了 /synology-photo 路徑可以查看資料，這時在 immich 網頁設定上，直接設定外部位置在 /synology-photo ，就可以讓 immich 掃描到照片資料來分析了&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;然而，對於資料管理上會想要慢慢實驗，例如少量的把目錄資料加入到 immich 外部圖庫，且不需要一直改 YAML volumes？通常對系統熟悉的，就會想試試 symbolic link 架構，還能避免 NAS 上有重複資料佔著空間，然而，symbolic link 在 Docker 環境上有使用限制，有一些討論串：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;&lt;a href="https://www.reddit.com/r/immich/comments/1dw2r4q/how_to_get_immich_to_follow_symlinks_for_external/" rel="nofollow" target="_blank"&gt;reddit: How to get immich to follow symlinks for external library&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/immich-app/immich/issues/6311" rel="nofollow" target="_blank"&gt;[BUG] External library doesn't follow relative symlinks #6311&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;我這邊的解法，其實是設法先排除 Docker 限制，只要繞過限制後，Symbolic link 還是可以使用的。目前的設計就先在 ~/Photos/ 建立一個目錄，如 immich ，接著在裡面建立 Symbolic&amp;nbsp;link 到上一層 ~/Photos 想要的資料就好，而掛載到 Docker 裡仍維持在 ~/Photos 位置，而 immich 網頁上就設定 /synology-photo/immich 即可。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;總結一下 Symbolic link 使用資訊：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;Docker volumes YAML 設定不變，概念上要把 Symbolic link 來源也包覆到&lt;/li&gt;&lt;ul&gt;&lt;li&gt;/var/services/homes/UserID/Photos:/synology-photo:ro&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;建立 /var/services/homes/UserID/Photos/immich 目錄&lt;/li&gt;&lt;li&gt;在 immich 網頁上，將外部圖庫設定在 /synology-photo/immich&lt;/li&gt;&lt;li&gt;未來想動態將資料交給 immich 服務時，就只須在 /var/services/homes/UserID/Photos/immich 內建立向上一層的目錄，確保那些享用 symbolic link 的資料都有在 Docker volumes 內即可&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;UserID@NAS:~/Photos/immich$ ls -l&lt;/div&gt;&lt;div&gt;lrwxrwxrwx+ 1 UserID users 27 Mar&amp;nbsp; 6 23:25 '2020-01-01' -&amp;gt; '../2020-01-01/'&lt;/div&gt;&lt;div&gt;lrwxrwxrwx&amp;nbsp; 1 UserID users 24 Mar&amp;nbsp; 6 23:28 '2021-02-02' -&amp;gt; '../2021-02-02/'&lt;/div&gt;&lt;div&gt;lrwxrwxrwx&amp;nbsp; 1 UserID users 27 Mar&amp;nbsp; 6 23:28 '2022-03-03' -&amp;gt; '../2022-03-03/'&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;收工&lt;/div&gt;</description><link>http://blog.changyy.org/2025/03/docker-synology-nas-immich-symbolic.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpMsbvIZ3L-PQXSrvnsip-74ZhJIf5-MkLX0B-WUjkjUu4NS_gSAXKE5oTNRNmYEDfdTHjYiWbwAOAjyyMII7tnNGOVKrkty74tdcFZf8O2DxBiBvCz1ifFD6lHq4ows-qWscaZN5X0V2WbixybJANtMtP4K0XdRdSir1Qf5pvrBIB5K503SH6v5td6EOg/s72-w640-h314-c/synology-docker-immich-00.png" width="72"/><thr:total>0</thr:total><enclosure length="3338" type="application/octet-stream" url="https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml"/></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-6824264633754612220</guid><pubDate>Tue, 25 Feb 2025 17:07:00 +0000</pubDate><atom:updated>2025-02-26T01:16:29.738+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">0x207</category><category domain="http://www.blogger.com/atom/ns#">24.04</category><category domain="http://www.blogger.com/atom/ns#">desktop</category><category domain="http://www.blogger.com/atom/ns#">lock</category><category domain="http://www.blogger.com/atom/ns#">macos</category><category domain="http://www.blogger.com/atom/ns#">RDP</category><category domain="http://www.blogger.com/atom/ns#">remote desktop</category><category domain="http://www.blogger.com/atom/ns#">ubuntu</category><category domain="http://www.blogger.com/atom/ns#">Windows app</category><title>[macOS] 使用 Windows APP (原 Microsoft RDP app) 遠端登入 Ubuntu 24.04 Desktop 遠端桌面 及 Error code: 0x207 問題排除 @ macOS 15.3.1</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ40UfhK8mpJoHDedBpT3e77fnjTEPLdwHe-LE5vE_HS0c7vRKSEVGDEmB8hvwiockMBzGGYXeBz3sF_Ks-vLaMY8AluOi8v5p6LxWQwc2hLd34Fv2EimOLLhM9IiMHnJOD9optE08pefizhkUynVgsIg057x7y4sQ45ByoOH6E767VjYRPCbtteXq8mHY/s1736/ubuntu-rdp-desktop-sharing.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1064" data-original-width="1736" height="392" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ40UfhK8mpJoHDedBpT3e77fnjTEPLdwHe-LE5vE_HS0c7vRKSEVGDEmB8hvwiockMBzGGYXeBz3sF_Ks-vLaMY8AluOi8v5p6LxWQwc2hLd34Fv2EimOLLhM9IiMHnJOD9optE08pefizhkUynVgsIg057x7y4sQ45ByoOH6E767VjYRPCbtteXq8mHY/w640-h392/ubuntu-rdp-desktop-sharing.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;把自己的 Pi 5 安裝 Ubuntu 24.04 Desktop 後，原本想要裝 VNC 來提供遠端桌面的，搞了半天才發現已經有內建的 RDP 服務，接著當然就是在 macOS 上找尋很多年前已裝過的 Microsoft Windows Remote Desktop app ，找了半天也沒找到，最後才發現 macOS App Store 已更名為 Windows App&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;第一次在 macOS 上 Windows APP 登入到 Ubuntu 24.04 Desktop 是正常的，但很詭異的是之後一直會收到錯誤訊息：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;We couldn't connect to the remote PC. This might be due to an expired password. If this keeps happening, contact your network administrator for assistance. Error code: 0x207&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;我也是趁這次才知道，原來 Ubuntu 24.04 Desktop 內建的 RDP 服務，必須使用者先登入過桌面才行，並且鎖住螢幕也不行。事後發現有一連串的方式解套。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxDtsnF58w46AAzjyNYLDiQs49VQuaMKgJKINImgwOfzYTDVG4_Woy2tSE_myGuLC9H0tczobREW90DC_7PHNgRiLSL3lFvTgnzNw8d9jl9Ql6G1J8sIxGQtEYdESF61XHY4GARulQ3m9gAxl7BBH0JXDVkfNzjxRt82uSPOvriAQk7njbnspTnWQKVY8D/s1738/extension-manager.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1118" data-original-width="1738" height="412" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxDtsnF58w46AAzjyNYLDiQs49VQuaMKgJKINImgwOfzYTDVG4_Woy2tSE_myGuLC9H0tczobREW90DC_7PHNgRiLSL3lFvTgnzNw8d9jl9Ql6G1J8sIxGQtEYdESF61XHY4GARulQ3m9gAxl7BBH0JXDVkfNzjxRt82uSPOvriAQk7njbnspTnWQKVY8D/w640-h412/extension-manager.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;首先是使用者必須先登入過桌面，那就開啟自動登入就好，剛好在 Pi 5 實驗裝置不會踩到資安議題。接著是鎖住螢幕的問題，有一派是調整成不自動鎖住，此時發現還可以透過額外安裝 GNOME Extension Manager，並且在裡頭下載 Allow Locked Remote Desktop 就好，設定好重啟。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;$ sudo apt install gnome-shell-extension-manager&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;最後，關於 macOS 上第二次起登入 Ubuntu Desktop 總是碰到的 Error code: 0x207 問題，神人解法是先把設定檔匯出，接著修改關鍵設定，把 "use redirection server name:i:0" 更新成 "use redirection server name:i:1" ，再匯入回去即可搞定。有些討論串認為是 macOS - Windows app 的 bug&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVLYN2OGzt0S-BMi6zRljsrcxgWMM8iCH7km-lH_-hffZbcS72gKT4dK_COm88V_tkvogJplGr0tjg1RvmYQLKud-lpeXamp4yjA2YanB0TcH22lBoLrj4ZkQ_sjKbROr_4jYIBhKBMuavIf53mh47XqMoo0TSBaeSFfYeByTp3gDdWW-OJtS5H7ZvNoKS/s1534/macos-windows-app.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1004" data-original-width="1534" height="418" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVLYN2OGzt0S-BMi6zRljsrcxgWMM8iCH7km-lH_-hffZbcS72gKT4dK_COm88V_tkvogJplGr0tjg1RvmYQLKud-lpeXamp4yjA2YanB0TcH22lBoLrj4ZkQ_sjKbROr_4jYIBhKBMuavIf53mh47XqMoo0TSBaeSFfYeByTp3gDdWW-OJtS5H7ZvNoKS/w640-h418/macos-windows-app.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;搞定&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;ref:&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;&lt;a href="https://www.reddit.com/r/Ubuntu/comments/1fqmicf/remote_login_to_ubuntu_desktop_2404_from_macos/" rel="nofollow" target="_blank"&gt;Remote Login to Ubuntu Desktop 24.04 from macOS&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://learn.microsoft.com/zh-tw/windows-server/remote/remote-desktop-services/clients/remote-desktop-uri" rel="nofollow" target="_blank"&gt;遠端桌面 URI 配置&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description><link>http://blog.changyy.org/2025/02/macos-rdp-windows-app-to-ubuntu-desktop-24.04-.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ40UfhK8mpJoHDedBpT3e77fnjTEPLdwHe-LE5vE_HS0c7vRKSEVGDEmB8hvwiockMBzGGYXeBz3sF_Ks-vLaMY8AluOi8v5p6LxWQwc2hLd34Fv2EimOLLhM9IiMHnJOD9optE08pefizhkUynVgsIg057x7y4sQ45ByoOH6E767VjYRPCbtteXq8mHY/s72-w640-h392-c/ubuntu-rdp-desktop-sharing.png" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-6931363595164739523</guid><pubDate>Sun, 23 Feb 2025 16:19:00 +0000</pubDate><atom:updated>2025-02-24T00:19:34.700+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI Kit</category><category domain="http://www.blogger.com/atom/ns#">Mini Tower kit</category><category domain="http://www.blogger.com/atom/ns#">pi</category><category domain="http://www.blogger.com/atom/ns#">Pi5</category><category domain="http://www.blogger.com/atom/ns#">Raspberry</category><title>[Linux] 樹莓派5開發筆記 - Raspberry Pi 5 + Pi AI kit + Pi Mini Tower kit @ Ubuntu 24.04 Desktop</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAckawkMqTFNIe_GIzSebp5snCWRfzO0Eka9DXtlNacEv0j6SCmULRxhUulwrheuAUkelEYQUR8txxgRF_Ymuux-Nl6iqvzaws9waIJIdfioUef-JQDjECCc5uo5Njm7KOWWzTHVQ9mOe75XORhnFgTe-Yox3IC3gj1pxdQaEiocs6eJA4MmmJME6HrwRP/s1024/mini-tower-03.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="768" data-original-width="1024" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAckawkMqTFNIe_GIzSebp5snCWRfzO0Eka9DXtlNacEv0j6SCmULRxhUulwrheuAUkelEYQUR8txxgRF_Ymuux-Nl6iqvzaws9waIJIdfioUef-JQDjECCc5uo5Njm7KOWWzTHVQ9mOe75XORhnFgTe-Yox3IC3gj1pxdQaEiocs6eJA4MmmJME6HrwRP/w640-h480/mini-tower-03.jpeg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;二月初手癢，敗了個 Raspberry Pi AI kit Hailo-8 ，接著發現以前買的 Pi 5 小機殼不能用了，裸機運行了一下，就手癢找找有沒有比較大的機殼，就看到了 樹莓派 5 塔式散熱器外殼組 | Raspberry Pi 5 Mini Tower Kit with ICE Tower ，接著再去蝦皮逛一眼，有看到遠方發貨的，就試了一下。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDClo-xOxQEtcwYGuGOiqAhZ723dDkZCLf24XbUeBQEl5vujbmazvRsqL6RekLYzZH-KT_o2bdTTzBMgTcW6OMAl4sf07eD2XIZxFhOwGPrPzXtxXwG8I7jpXkki0l4mBmjocx3q7trE0azBrxun4NdUWzxZz92g8-vgVgmmBY-X_fWFCcq52qGVZhp-vA/s1024/mini-tower-01.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="768" data-original-width="1024" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDClo-xOxQEtcwYGuGOiqAhZ723dDkZCLf24XbUeBQEl5vujbmazvRsqL6RekLYzZH-KT_o2bdTTzBMgTcW6OMAl4sf07eD2XIZxFhOwGPrPzXtxXwG8I7jpXkki0l4mBmjocx3q7trE0azBrxun4NdUWzxZz92g8-vgVgmmBY-X_fWFCcq52qGVZhp-vA/w640-h480/mini-tower-01.jpeg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;實際上排線是非常緊繃地連著：&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE4fAeEFiRfz-TKWZVi3M4hyzyaRV7oLcxB0T9na76HhMMo1tIJ4SJ_XbwO0Z9xa-sxez63rfycOZpQFBPEqmiXgXYZmNGu9QxQ3H8dY5FcuWScjt-ZOU7ZnvJ17LSIfcKkHqt3t2nDI2JeBy1a0NqVwTNn6DOa7KmrGExVIdxTUi10cDKKTBeEpdIfQ29/s1024/mini-tower-02.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="768" data-original-width="1024" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE4fAeEFiRfz-TKWZVi3M4hyzyaRV7oLcxB0T9na76HhMMo1tIJ4SJ_XbwO0Z9xa-sxez63rfycOZpQFBPEqmiXgXYZmNGu9QxQ3H8dY5FcuWScjt-ZOU7ZnvJ17LSIfcKkHqt3t2nDI2JeBy1a0NqVwTNn6DOa7KmrGExVIdxTUi10cDKKTBeEpdIfQ29/w640-h480/mini-tower-02.jpeg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;有一邊的腳架不鎖（無法鎖）：&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAsyTtveDzR5e2FebuOeiToCikM79sIizhb_SKrw1POQqF-rKoTYIxG4Igi7uOprmpc9sn5kDbVjEaERbU-zf9HBtvyKUacqK7k6w-58SgQASOP1SHJPYSqRGi3uY2RzaV_-hSc5O6OlmwLswRZ-ntdRp4BfWakmVN5JINxFtZTsZkkJJnjNoppix3ImZ1/s1024/mini-tower-04.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="768" data-original-width="1024" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAsyTtveDzR5e2FebuOeiToCikM79sIizhb_SKrw1POQqF-rKoTYIxG4Igi7uOprmpc9sn5kDbVjEaERbU-zf9HBtvyKUacqK7k6w-58SgQASOP1SHJPYSqRGi3uY2RzaV_-hSc5O6OlmwLswRZ-ntdRp4BfWakmVN5JINxFtZTsZkkJJnjNoppix3ImZ1/w640-h480/mini-tower-04.jpeg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxE_Yw6ufC70g6ZmQPdJ7YlFWfyAOVVlwYBDSHJPPmT8rlKvPeL56-u2PxKXlkl52iL8ePLN3eSHpxvaQby_-ZLhH1ED9y31CnvvmcyqWKgOUcYoTIarZB7O2tIEAmnYjb4dg9vy2L4Q0Cbt_4OU_v8hhOpfa9Wmocnlq1sENZpN4_UDqr0h0ydQ_I35rB/s1024/mini-tower-05.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="768" data-original-width="1024" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxE_Yw6ufC70g6ZmQPdJ7YlFWfyAOVVlwYBDSHJPPmT8rlKvPeL56-u2PxKXlkl52iL8ePLN3eSHpxvaQby_-ZLhH1ED9y31CnvvmcyqWKgOUcYoTIarZB7O2tIEAmnYjb4dg9vy2L4Q0Cbt_4OU_v8hhOpfa9Wmocnlq1sENZpN4_UDqr0h0ydQ_I35rB/w640-h480/mini-tower-05.jpeg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;最終就這樣被我硬拼了出來，上方的 Pi AI kit 的架子有兩隻腳是懸空的，懸空那面剛好有排線針腳固定著也不太會晃，就這樣胡亂用了起來。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這邊就簡單的完成 Mini Tower Kit 的面板(OLED)初始化，順便小改 code 來達成顯示 IP 和收到關機時可正常退出使螢幕變黑。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO_WwnRz-k0uxUEaIDiTWhXb_zLtYeu-QdvtiqGnPN3bn4j3Q1BEJykQVctazUI8Sr8zT8BlKwKv3EV4GlEqNEi_YOizC6Vzp4NypJhpz3ovlKLeXCsgqMPYfLIxw5dIcpgyiw4MY22EmhjwZn8Z5CfxV5zBxgyAiD5Cyhn-OaOb5u3YsGvKOo609AGpJz/s1024/mini-tower-06.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="768" data-original-width="1024" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO_WwnRz-k0uxUEaIDiTWhXb_zLtYeu-QdvtiqGnPN3bn4j3Q1BEJykQVctazUI8Sr8zT8BlKwKv3EV4GlEqNEi_YOizC6Vzp4NypJhpz3ovlKLeXCsgqMPYfLIxw5dIcpgyiw4MY22EmhjwZn8Z5CfxV5zBxgyAiD5Cyhn-OaOb5u3YsGvKOo609AGpJz/w640-h480/mini-tower-06.jpeg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;目前就包成一個 script 來運行:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;user@user-desktop:~$ lsb_release -a&lt;/div&gt;&lt;div&gt;No LSB modules are available.&lt;/div&gt;&lt;div&gt;Distributor ID:&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;Ubuntu&lt;/div&gt;&lt;div&gt;Description:&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;Ubuntu 24.04.2 LTS&lt;/div&gt;&lt;div&gt;Release:&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;24.04&lt;/div&gt;&lt;div&gt;Codename:&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;noble&lt;/div&gt;&lt;div&gt;user@user-desktop:~$ curl https://raw.githubusercontent.com/changyy/study-pi5/refs/heads/main/script/pi5-mini-tower-kit-with-oled-only.sh &amp;gt; /tmp/pi5-mini-tower-kit-with-oled-only.sh&lt;/div&gt;&lt;div&gt;&amp;nbsp; % Total&amp;nbsp; &amp;nbsp; % Received % Xferd&amp;nbsp; Average Speed&amp;nbsp; &amp;nbsp;Time&amp;nbsp; &amp;nbsp; Time&amp;nbsp; &amp;nbsp; &amp;nbsp;Time&amp;nbsp; Current&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Dload&amp;nbsp; Upload&amp;nbsp; &amp;nbsp;Total&amp;nbsp; &amp;nbsp;Spent&amp;nbsp; &amp;nbsp; Left&amp;nbsp; Speed&lt;/div&gt;&lt;div&gt;100&amp;nbsp; 6184&amp;nbsp; 100&amp;nbsp; 6184&amp;nbsp; &amp;nbsp; 0&amp;nbsp; &amp;nbsp; &amp;nbsp;0&amp;nbsp; 11814&amp;nbsp; &amp;nbsp; &amp;nbsp; 0 --:--:-- --:--:-- --:--:-- 11801&lt;/div&gt;&lt;div&gt;user@user-desktop:~$ bash /tmp/pi5-mini-tower-kit-with-oled-only.sh&amp;nbsp;&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;收工&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;有興趣可以在參考這邊：&lt;a href="https://github.com/changyy/study-pi5/blob/main/script/pi5-mini-tower-kit-with-oled-only.sh" rel="nofollow" target="_blank"&gt;github.com/changyy/study-pi5/blob/main/script/pi5-mini-tower-kit-with-oled-only.sh&lt;/a&gt;&lt;/div&gt;</description><link>http://blog.changyy.org/2025/02/linux-raspberry-pi-5-ai-kit-mini-tower-kit-case.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAckawkMqTFNIe_GIzSebp5snCWRfzO0Eka9DXtlNacEv0j6SCmULRxhUulwrheuAUkelEYQUR8txxgRF_Ymuux-Nl6iqvzaws9waIJIdfioUef-JQDjECCc5uo5Njm7KOWWzTHVQ9mOe75XORhnFgTe-Yox3IC3gj1pxdQaEiocs6eJA4MmmJME6HrwRP/s72-w640-h480-c/mini-tower-03.jpeg" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-2603422200531306514</guid><pubDate>Fri, 21 Feb 2025 14:08:00 +0000</pubDate><atom:updated>2025-02-21T22:08:03.680+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ai</category><category domain="http://www.blogger.com/atom/ns#">pack</category><category domain="http://www.blogger.com/atom/ns#">prompt</category><category domain="http://www.blogger.com/atom/ns#">promptpack-for-code</category><category domain="http://www.blogger.com/atom/ns#">review</category><title>Python 開發筆記 - promptpack-for-code 一個將專案的目錄結構和指定檔案輸出單一文字檔，讓 AI 點評跟建議的小工具</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRdphjmKVMRbXWe1bevumYR7_XQ6WX2PFxCMTzKYwQ6CBR0ReMIvGwNJQ8fmAJPx3eRuwVvyIVXsOfhbjOYlOKp-BFoQdFO8GizXU6NpJxLA_GwYNB5ulfhmz3BDajyYhII7zh1nxRPrJ2I2Gwu1G2Byw0cwz6-KsISZJMiU0CJIMmOwFE_vk9X-NFZekm/s2282/Screenshot%202025-02-21%20at%2022.00.41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1444" data-original-width="2282" height="404" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRdphjmKVMRbXWe1bevumYR7_XQ6WX2PFxCMTzKYwQ6CBR0ReMIvGwNJQ8fmAJPx3eRuwVvyIVXsOfhbjOYlOKp-BFoQdFO8GizXU6NpJxLA_GwYNB5ulfhmz3BDajyYhII7zh1nxRPrJ2I2Gwu1G2Byw0cwz6-KsISZJMiU0CJIMmOwFE_vk9X-NFZekm/w640-h404/Screenshot%202025-02-21%20at%2022.00.41.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;近半年已經極高的比例都會用 claude.ai 做事，最近用量太大後，再加上 grok.com 免費中，就想到如果碰到付費額度用原被凍時，該怎樣快速換另一家 AI 服務使用，也可以避免與 AI 輔助努力了老半天，重啟對話又要打得落落長的內容。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFA6uUIhec6Uin9wZEK1S3TmLv67ynrt4iLx34ackPRJGeP4If5qcEvqohwLFtayuaPN1BUneOs71IqMcvOO0ZPgJVtj6zzy3yYZLgzTIrsZea89X46NlNCWLxkMCnStFwfeQP-W1OqJZbrDX1dowGgaVxYIvOPPHEqQ3vOkbR2Xf8e1PIBVYjsjQrA5RY/s1748/claude-ai-limit.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1034" data-original-width="1748" height="378" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFA6uUIhec6Uin9wZEK1S3TmLv67ynrt4iLx34ackPRJGeP4If5qcEvqohwLFtayuaPN1BUneOs71IqMcvOO0ZPgJVtj6zzy3yYZLgzTIrsZea89X46NlNCWLxkMCnStFwfeQP-W1OqJZbrDX1dowGgaVxYIvOPPHEqQ3vOkbR2Xf8e1PIBVYjsjQrA5RY/w640-h378/claude-ai-limit.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;因此就開發了一個小工具：&lt;a href="https://pypi.org/project/promptpack-for-code/" rel="nofollow" target="_blank"&gt;promptpack-for-code&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;他的用法就是把指定專案的目錄結構、指定的程式碼輸出成 output.txt ，接著就可以上傳 output.txt 跟 AI 互動，成果包括可以請他點評哪邊可以優化、撰寫系統架構、教學文件等等，十分方便&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% python3 -m venv venv&lt;/div&gt;&lt;div&gt;% source venv/bin/activate&lt;/div&gt;&lt;div&gt;(venv) /tmp % pip install promptpack-for-code&lt;/div&gt;&lt;div&gt;% git clone https://github.com/changyy/py-promptpack-for-code&lt;/div&gt;&lt;div&gt;% promptpack-for-code py-promptpack-for-code -r py-promptpack-for-code&amp;nbsp;&lt;/div&gt;&lt;div&gt;Successfully created output.txt&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;問問此時此刻(2025-02-21)最強的 Grok 3 AI：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;### 專案概述&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;`py-promptpack-for-code` 是一個 Python 命令列工具，旨在將多個程式碼檔案或目錄中的檔案打包成一個結構化的單一文字檔，特別針對與 AI 語言模型進行程式碼審查或分析的需求進行優化。這個工具的主要功能包括：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;1. **檔案結構生成**：根據指定的根目錄生成樹狀目錄結構，方便理解專案的組織。&lt;/div&gt;&lt;div&gt;2. **內容整合**：將指定目錄中的程式碼檔案內容合併到輸出檔案，並保留相對路徑。&lt;/div&gt;&lt;div&gt;3. **過濾與忽略**：支援自訂忽略模式（例如 `.git`、`.pyc` 等），避免不必要的檔案內容。&lt;/div&gt;&lt;div&gt;4. **進度顯示**：提供進度條選項，適合處理大型專案時使用。&lt;/div&gt;&lt;div&gt;5. **易於與 AI 整合**：輸出格式規範且結構化，便於直接輸入到 AI 聊天平台進行分析。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;### 專案評論&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#### 優點&lt;/div&gt;&lt;div&gt;1. **實用性強**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 該工具解決了一個具體且常見的痛點：如何快速準備程式碼供 AI 分析。它不僅合併檔案，還提供了目錄結構，這對理解上下文非常有幫助。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 支援多目錄處理、進度條、強制覆蓋等功能，滿足不同使用場景的需求。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;2. **結構清晰**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 專案採用標準 Python 包結構（`src/`、`tests/` 等），易於維護和擴展。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 文件（如 `README.md` 和 `LICENSE`）完整，提供了詳細的使用說明和授權資訊。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;3. **測試覆蓋良好**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- `tests/test_core.py` 包含多個單元測試，涵蓋核心功能（樹狀結構生成、檔案處理、忽略模式等），提高了程式碼的可靠性。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;4. **部署與分享便利**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 透過 `setup.py` 和 GitHub Actions（`python-publish.yml`）實現 PyPI 發佈，使用者只需 `pip install` 即可使用。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- MIT 許可證允許廣泛的開源使用和修改。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;5. **效能考慮**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 使用 `ThreadPoolExecutor` 實現並行處理檔案，對於大型專案具有一定的效能優勢。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#### 不足之處&lt;/div&gt;&lt;div&gt;1. **功能單一性**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 目前工具主要專注於生成文字輸出，缺乏更高級的分析或格式化選項（例如 JSON 輸出或程式碼摘要生成）。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 對於非文字檔案的處理較為簡單（僅跳過），可能無法滿足需要處理混合內容的需求。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;2. **錯誤處理有限**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 雖然有基本的日誌記錄（`logging`），但對於檔案編碼錯誤（`UnicodeDecodeError`）或權限問題的處理僅限於跳過或警告，缺乏更靈活的應對策略。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;3. **文件不夠詳盡**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- `README.md` 提供了基本用法，但對於進階功能（例如自訂忽略模式的具體範例）或輸出檔案的詳細格式說明不夠深入。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 缺少 API 文件，對於希望程式化調用的開發者不夠友好。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;4. **忽略模式的靈活性**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 雖然支援 `fnmatch` 通配符，但忽略模式僅限於檔案名稱，無法根據檔案內容或大小進行過濾。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;### 優化建議&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;1. **增加輸出格式選項**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 除了目前的純文字輸出，新增 JSON 或 YAML 格式選項，讓使用者可以更方便地將結果用於其他工具或程式化處理。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 例如：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;```json&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;"tree": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;"project-name": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;"src": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;"main.py": "def main():\n&amp;nbsp; &amp;nbsp; print('Hello')\n",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;"utils": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;"helper.py": "def helper():\n&amp;nbsp; &amp;nbsp; return True\n"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;```&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;2. **增強錯誤處理與回饋**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 提供選項讓使用者決定如何處理非 UTF-8 檔案（例如轉換編碼或以二進位模式顯示部分內容）。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 對於權限錯誤，記錄到輸出檔案中，而不是僅在日誌中警告，這樣使用者能更容易追蹤問題。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;3. **擴展忽略模式功能**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 支援基於檔案大小、修改時間或內容關鍵字的過濾。例如：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;```bash&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;promptpack-for-code /path/to/src --ignore-size "&amp;gt;10MB" --ignore-modified "&amp;lt;2023-01-01"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;```&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 允許從外部檔案（如 `.gitignore`）載入忽略模式，增強與現有工作流的兼容性。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;4. **改善文件與範例**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 在 `README.md` 中新增更多實際範例，例如處理大型專案或與 AI 工具（如 ChatGPT 或 GitHub Copilot）配合的具體流程。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 提供一份獨立的 `CONTRIBUTING.md`，詳細說明如何提交問題或貢獻程式碼。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;5. **新增摘要功能**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 提供選項生成程式碼摘要（例如每個檔案的前幾行或函數簽名），減少輸出檔案的大小，方便快速預覽。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;```bash&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;promptpack-for-code /path/to/src --summary&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;```&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;6. **支援增量更新**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 增加一個模式，僅處理自上次運行以來有變更的檔案（例如根據檔案修改時間戳記），提高處理效率。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;```bash&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;promptpack-for-code /path/to/src --incremental&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;```&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;7. **UI 或互動模式**：&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;- 開發一個簡單的 TUI（文字使用者介面）或互動模式，讓使用者在命令列中動態選擇要處理的目錄或檔案。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;### 總結&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;`py-promptpack-for-code` 是一個設計良好且實用的工具，特別適合需要與 AI 進行程式碼分析的開發者。其核心功能已經相當成熟，但在靈活性、錯誤處理和文件方面仍有提升空間。透過上述建議，這個專案可以進一步滿足更廣泛的使用場景，並提升使用者的體驗。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如果你有具體的使用需求或想討論某個優化方向，我可以進一步提供更詳細的建議或程式碼範例！&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;</description><link>http://blog.changyy.org/2025/02/python-promptpack-for-code-ai-review-usage.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRdphjmKVMRbXWe1bevumYR7_XQ6WX2PFxCMTzKYwQ6CBR0ReMIvGwNJQ8fmAJPx3eRuwVvyIVXsOfhbjOYlOKp-BFoQdFO8GizXU6NpJxLA_GwYNB5ulfhmz3BDajyYhII7zh1nxRPrJ2I2Gwu1G2Byw0cwz6-KsISZJMiU0CJIMmOwFE_vk9X-NFZekm/s72-w640-h404-c/Screenshot%202025-02-21%20at%2022.00.41.png" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-5325341909243929792</guid><pubDate>Mon, 17 Feb 2025 15:59:00 +0000</pubDate><atom:updated>2025-02-18T11:27:23.388+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">aws</category><category domain="http://www.blogger.com/atom/ns#">awscli</category><category domain="http://www.blogger.com/atom/ns#">bash</category><category domain="http://www.blogger.com/atom/ns#">cli</category><category domain="http://www.blogger.com/atom/ns#">cluster</category><category domain="http://www.blogger.com/atom/ns#">container</category><category domain="http://www.blogger.com/atom/ns#">docker</category><category domain="http://www.blogger.com/atom/ns#">ecs</category><category domain="http://www.blogger.com/atom/ns#">enableExecuteCommand</category><category domain="http://www.blogger.com/atom/ns#">exec</category><category domain="http://www.blogger.com/atom/ns#">login</category><category domain="http://www.blogger.com/atom/ns#">service</category><category domain="http://www.blogger.com/atom/ns#">session</category><category domain="http://www.blogger.com/atom/ns#">ssh</category><title>Docker 開發筆記 - 使用 aws cli 和 Docker Exec 進入 AWS ECS Container @ macOS</title><description>&lt;div&gt;這個議題比我想像中麻煩了點，讓我回憶起 2009 年寫的 AWS 筆記，那時同事一開始還只在用 Firefox extension 管理 AWS EC2 呢 XD 我覺得這理論上要能都透過網頁搞定才對，先把目前研究的過程筆記一下。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;總之，要能像 ssh 遠端(docker exec -it ContainerID bash)進去 AWS ECS container 的關鍵之處：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;使用 awscli 做事&lt;/li&gt;&lt;li&gt;AWS ECS 的 Task 定義，基礎設施需求 -&amp;gt; 任務角色，需要指定一下角色，例如 ecsTaskExecutionRole&lt;/li&gt;&lt;li&gt;AWS IAM -&amp;gt; ecsTaskExecutionRole -&amp;gt; 添加&amp;nbsp;AmazonSSMManagedInstanceCore 權限&lt;/li&gt;&lt;li&gt;AWS ECS -&amp;gt; Cluster -&amp;gt; Service ，需要用 awscli 啟動 enable-execute-command ，並且重新更新服務，使之生效&lt;/li&gt;&lt;li&gt;使用 aws 指令登入&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;首先，先下載 awscli 並且版本要夠新：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/zh_tw/cli/latest/userguide/getting-started-install.html" rel="nofollow" target="_blank"&gt;docs.aws.amazon.com/zh_tw/cli/latest/userguide/getting-started-install.html&lt;/a&gt;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://awscli.amazonaws.com/AWSCLIV2.pkg" rel="nofollow" target="_blank"&gt;awscli.amazonaws.com/AWSCLIV2.pkg&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;安裝後檢查版本，版本太低會無法完成任務：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% aws --version&lt;/div&gt;&lt;div&gt;aws-cli/2.24.5 Python/3.12.6 Darwin/24.3.0 exe/x86_64&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著還要安裝 Session Manager plugin，此例紀錄 Mac with Apple silicon 版：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/install-plugin-macos-overview.html" rel="nofollow" target="_blank"&gt;docs.aws.amazon.com/systems-manager/latest/userguide/install-plugin-macos-overview.html&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac_arm64/sessionmanager-bundle.zip" -o "sessionmanager-bundle.zip"&lt;/div&gt;&lt;div&gt;% unzip sessionmanager-bundle.zip&lt;/div&gt;&lt;div&gt;% sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;基本的環境已準備好了，下一刻是查看自己的 AWS ECS 的任務定義是否有把 任務角色 設定好，這部就維持用網頁吧：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaumAeZE7OtdeSxiJqos3wRBXs9XQv-foqgUw3De3mwBHVIaWXzaD2OctXAxR2z7cfVQwhXj02lnZeyBwHYunxOTZ3iSv-5xkpB0yr5W6PbWyX0BsJmw_LzE4iAFCaiaSUXWUIfX419kUbypNKLdXRfgwihbcXj3LGTMfJZYtP0cJFUPbtJj6pytTvR1cY/s2396/aws-ecs-task-web-ui.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1340" data-original-width="2396" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaumAeZE7OtdeSxiJqos3wRBXs9XQv-foqgUw3De3mwBHVIaWXzaD2OctXAxR2z7cfVQwhXj02lnZeyBwHYunxOTZ3iSv-5xkpB0yr5W6PbWyX0BsJmw_LzE4iAFCaiaSUXWUIfX419kUbypNKLdXRfgwihbcXj3LGTMfJZYtP0cJFUPbtJj6pytTvR1cY/w640-h358/aws-ecs-task-web-ui.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;若你的 AWS ECS 上定義的 Task 只有一個，也可以偷懶靠 awscli 操作（在此就不贅述 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION 部分），會用到的指令：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;aws ecs list-task-definition-families --status ACTIVE&lt;/div&gt;&lt;div&gt;aws ecs list-task-definitions --family-prefix webapp&lt;/div&gt;&lt;div&gt;aws ecs describe-task-definition --task-definition webapp:3&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;連續技，快速檢查 taskRoleArn 跟 executionRoleArn：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% NamespaceID=$(aws ecs list-task-definition-families --status ACTIVE | jq -r '.families[0]') ; echo "Namespace: $NamespaceID" ; TaskID=$(aws ecs list-task-definitions --family-prefix "$NamespaceID" | jq -r '.taskDefinitionArns[0]') ; echo "Task: $TaskID" ; aws ecs describe-task-definition --task-definition "$TaskID" | jq ".taskDefinition | { taskRoleArn: .taskRoleArn, executionRoleArn: .executionRoleArn}"&lt;/div&gt;&lt;div&gt;Namespace: myapp-task&lt;/div&gt;&lt;div&gt;Task: arn:aws:ecs:ap-northeast-1:####:task-definition/myapp-task:2&lt;/div&gt;&lt;div&gt;{&lt;/div&gt;&lt;div&gt;&amp;nbsp; "taskRoleArn": "arn:aws:iam::####:role/ecsTaskExecutionRole",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "executionRoleArn": "arn:aws:iam::####:role/ecsTaskExecutionRole"&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接下來，若 AWS ECS Cluster 還沒有建立任何 Service，用指令查：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% aws ecs list-tasks --cluster myapp-cluster&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;{&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "taskArns": []&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;接著我們在 AWS ECS 網頁端起了一個 Service 名為 myapp-service ，在創建過程中沒看到啟用 enable-execute-command，這時在網頁上查看也是顯示 "ECS 執行: 關閉"&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBkA29AXTxlThPHYhBs0xKU-k3SH3GdD4yme2R6D-G16jl8IlE0GbKnILdWWlL_CyJDECnB7clxx4nCSCEM8Vkh00YGdqUWSszhIK95i6wyHCIG-0HxEo_CTcoInJo8dRL5ODHxjGKRNLHVZqXsI3m46lki0bM6DHdSH5B2wpfdOs2OruhGJZiSSelKhON/s1448/aws-ecs-service-task-status.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="854" data-original-width="1448" height="378" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBkA29AXTxlThPHYhBs0xKU-k3SH3GdD4yme2R6D-G16jl8IlE0GbKnILdWWlL_CyJDECnB7clxx4nCSCEM8Vkh00YGdqUWSszhIK95i6wyHCIG-0HxEo_CTcoInJo8dRL5ODHxjGKRNLHVZqXsI3m46lki0bM6DHdSH5B2wpfdOs2OruhGJZiSSelKhON/w640-h378/aws-ecs-service-task-status.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接下來用 aws cli 查詢：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% aws ecs list-tasks --cluster myapp-cluster&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;{&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "taskArns": [&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "arn:aws:ecs:ap-northeast-1:####:task/myapp-cluster/#TASKID#"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; ]&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;% TaskID=$(aws ecs list-tasks --cluster myapp-cluster | jq -r '.taskArns[0]') ; echo "TaskID: $TaskID" ; aws ecs describe-tasks --cluster myapp-cluster --tasks $TaskID | jq '.tasks[0] | { "clusterArn": .clusterArn, "taskArn": .taskArn, "taskDefinitionArn": .taskDefinitionArn, "group": .group, "healthStatus": .healthStatus, "desiredStatus": .desiredStatus, "enableExecuteCommand": .enableExecuteCommand, "containers-name": [.containers |.[] | { "name":.name, "runtimeId": .runtimeId} ] }'&lt;/div&gt;&lt;div&gt;TaskID: arn:aws:ecs:ap-northeast-1:####:task/myapp-cluster/#TASKID#&lt;/div&gt;&lt;div&gt;{&lt;/div&gt;&lt;div&gt;&amp;nbsp; "clusterArn": "arn:aws:ecs:ap-northeast-1:####:cluster/myapp-cluster",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "taskArn": "arn:aws:ecs:ap-northeast-1:####:task/myapp-cluster/#TASKID#",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:####:task-definition/myapp-task:2",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "group": "service:myapp-service",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "healthStatus": "HEALTHY",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "desiredStatus": "RUNNING",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "enableExecuteCommand": false,&lt;/div&gt;&lt;div&gt;&amp;nbsp; "containers-name": [&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "name": "php-fpm-docker",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "runtimeId": "#TASKID#-#ContainerID#"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "name": "web-docker",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "runtimeId": "#TASKID#-#ContainerID#"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; ]&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;可以看到 enableExecuteCommand 為 false&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這時候，如果透過 aws cli 來設法登入到 Container：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% aws ecs execute-command --cluster myapp-cluster --task #TASKID# --container #containers#name# --command "/bin/bash" --interactive&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;An error occurred (InvalidParameterException) when calling the ExecuteCommand operation: Unable to start session because the container doesn’t exist. Specify a valid container and try again.&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著，使用 aws cli 來啟動 enableExecuteCommand 吧：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% aws ecs update-service --cluster myapp-cluster --service arn:aws:ecs:ap-northeast-1:####:service/myapp-cluster/myapp-service --enable-execute-command&amp;nbsp;&lt;/div&gt;&lt;div&gt;{&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "service": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ...&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "enableExecuteCommand": true,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ...&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;可以看到 enableExecuteCommand 被標記成 true 了，這時還需要重新發布服務，可以重網頁去觸發，或是靠指令觸發：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;% aws ecs update-service --cluster myapp-cluster --service arn:aws:ecs:ap-northeast-1:####:service/myapp-cluster/myapp-service --force-new-deployment&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;當服務發布完畢後，在網頁上就可以看到改變，或是用指令在查一次：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% TaskID=$(aws ecs list-tasks --cluster myapp-cluster | jq -r '.taskArns[0]') ; echo "TaskID: $TaskID" ; aws ecs describe-tasks --cluster myapp-cluster --tasks $TaskID | jq '.tasks[0] | { "clusterArn": .clusterArn, "taskArn": .taskArn, "taskDefinitionArn": .taskDefinitionArn, "group": .group, "healthStatus": .healthStatus, "desiredStatus": .desiredStatus, "enableExecuteCommand": .enableExecuteCommand, "containers-name": [.containers |.[] | { "name":.name, "runtimeId": .runtimeId} ] }'&lt;/div&gt;&lt;div&gt;TaskID: arn:aws:ecs:ap-northeast-1:####:task/myapp-cluster/#TASKID#&lt;/div&gt;&lt;div&gt;{&lt;/div&gt;&lt;div&gt;&amp;nbsp; "clusterArn": "arn:aws:ecs:ap-northeast-1:####:cluster/myapp-cluster",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "taskArn": "arn:aws:ecs:ap-northeast-1:####:task/myapp-cluster/#TASKID#",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:####:task-definition/myapp-task:2",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "group": "service:myapp-service",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "healthStatus": "HEALTHY",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "desiredStatus": "RUNNING",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "enableExecuteCommand": true,&lt;/div&gt;&lt;div&gt;&amp;nbsp; "containers-name": [&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "name": "php-fpm-docker",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "runtimeId": "#TASKID#-#CONTAINERID#"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "name": "web-docker",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "runtimeId": "#TASKID#-#CONTAINERID#"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; ]&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如此，就可以正式遠端進去一下：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% aws ecs execute-command --cluster myapp-cluster --task #TASKID# --container web-docker --command "/bin/bash" --interactive&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Starting session with SessionId: ecs-execute-command-################&lt;/div&gt;&lt;div&gt;ip-123-45-6-123:/var/www/html#&amp;nbsp;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;收工&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;參考資料：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/zh_tw/AmazonECS/latest/developerguide/ecs-exec.html" rel="nofollow" target="_blank"&gt;Amazon ECS &amp;gt; 開發人員指南 &amp;gt; 使用 ECS Exec 監控 Amazon ECS 容器&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/zh_tw/systems-manager/latest/userguide/run-command.html" rel="nofollow" target="_blank"&gt;AWS Systems Manager &amp;gt; 使用者指南 &amp;gt; AWS Systems Manager Run Command&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</description><link>http://blog.changyy.org/2025/02/docker-exec-aws-ecs-container-bash-ssh.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaumAeZE7OtdeSxiJqos3wRBXs9XQv-foqgUw3De3mwBHVIaWXzaD2OctXAxR2z7cfVQwhXj02lnZeyBwHYunxOTZ3iSv-5xkpB0yr5W6PbWyX0BsJmw_LzE4iAFCaiaSUXWUIfX419kUbypNKLdXRfgwihbcXj3LGTMfJZYtP0cJFUPbtJj6pytTvR1cY/s72-w640-h358-c/aws-ecs-task-web-ui.png" width="72"/><thr:total>0</thr:total><enclosure length="41006875" type="binary/octet-stream" url="https://awscli.amazonaws.com/AWSCLIV2.pkg"/></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-7392810540368952361</guid><pubDate>Sun, 16 Feb 2025 13:23:00 +0000</pubDate><atom:updated>2025-02-17T10:37:19.955+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">aws</category><category domain="http://www.blogger.com/atom/ns#">cicd</category><category domain="http://www.blogger.com/atom/ns#">container</category><category domain="http://www.blogger.com/atom/ns#">deploy</category><category domain="http://www.blogger.com/atom/ns#">docker</category><category domain="http://www.blogger.com/atom/ns#">ecs</category><category domain="http://www.blogger.com/atom/ns#">gitlab</category><category domain="http://www.blogger.com/atom/ns#">gitlab.com</category><title>Docker 開發筆記 - AWS ECS 發布 Docker Container 服務和 Gitlab CI/CD 架構實踐</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidBrcDBSoB2B-BBeDfx8vLTh9kVLt0pm0CnYNbZ3ckDyIac9VPjd__Lgrm00AF83PoCx1mserYjqy0Pj1O0_RLuJoGCrmozCdyOzxd6BKOOAclIo1hB4zuoTvT-8puGUN2Q7MSd877bHSCYxmE02qJgDqLnf7XEp2rGfmdVMfbj8eNby3_tP2d7ckbrKO-/s2604/gitlab-pipelines.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="830" data-original-width="2604" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidBrcDBSoB2B-BBeDfx8vLTh9kVLt0pm0CnYNbZ3ckDyIac9VPjd__Lgrm00AF83PoCx1mserYjqy0Pj1O0_RLuJoGCrmozCdyOzxd6BKOOAclIo1hB4zuoTvT-8puGUN2Q7MSd877bHSCYxmE02qJgDqLnf7XEp2rGfmdVMfbj8eNby3_tP2d7ckbrKO-/w640-h204/gitlab-pipelines.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;上回在 &lt;a href="https://blog.changyy.org/2025/02/docker-gitlab-cicd-docker-image-amazon.html" rel="nofollow" target="_blank"&gt;Docker 開發筆記 - 從 Gitlab 觸發 CI/CD，製作 Docker Image 並發布到 Amazon Elastic Container Service (AWS ECS)&lt;/a&gt;&amp;nbsp;已經提到在 Gitlab.com 完成 Docker Images 的建置、打包、儲存至 Gitlab Container Registry 的架構，以及從 AWS ECS 上如何取得 Gitlab Container Registry 的資料，但沒有紀錄太多 AWS ECS 筆記，這次就完整順一次在 AWS ECS 發布服務時要經歷的事情。我們挑 AWS Japan Region (ap-northeast-1) 來做事。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;第一步，可以先在 AWS ECS 建立 Task，例如取名為 myapp-task，過程中會設置 "基礎設施需求" 和 數個 "容器" 設定。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvrj7Ev6qmWSsV_EfBY1uMBUM4TFSUICKdgB098Kij46ttHwFLLOBQ94nH7emsVVuw45teJeMQKhr2DckjmobQxAXEts99sunylZtc0_EUqU1vs6JVBwordjkRhvw2Wr1tjRyJAbGnS9my4F7WhKzd5fFzlmiwoeG-tuVXGcUKqX1xUgxhyoigqB9vIqRR/s3286/aws-esc-create-task.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1676" data-original-width="3286" height="326" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvrj7Ev6qmWSsV_EfBY1uMBUM4TFSUICKdgB098Kij46ttHwFLLOBQ94nH7emsVVuw45teJeMQKhr2DckjmobQxAXEts99sunylZtc0_EUqU1vs6JVBwordjkRhvw2Wr1tjRyJAbGnS9my4F7WhKzd5fFzlmiwoeG-tuVXGcUKqX1xUgxhyoigqB9vIqRR/w640-h326/aws-esc-create-task.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiypD3B5-O1WztHxNc5o0wEmA8fmtRF4R4Lo3J_Chs95BGU4dt7kSA_4FvHLKrYOG7GJ7PUZciLF2V4_dmbZCb-V4aKxiFhJqw9iFkpNWAq5GSS8fkb007JohRqKhEgy4yfqJG8gpgD0ATNNu5mnehGJwz3S0rAMPOo2U3cA8MZfv5QQ6L9Cojzr1Mi0VJW/s2678/aws-esc-create-task-01-create-container.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1732" data-original-width="2678" height="414" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiypD3B5-O1WztHxNc5o0wEmA8fmtRF4R4Lo3J_Chs95BGU4dt7kSA_4FvHLKrYOG7GJ7PUZciLF2V4_dmbZCb-V4aKxiFhJqw9iFkpNWAq5GSS8fkb007JohRqKhEgy4yfqJG8gpgD0ATNNu5mnehGJwz3S0rAMPOo2U3cA8MZfv5QQ6L9Cojzr1Mi0VJW/w640-h414/aws-esc-create-task-01-create-container.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在 基礎設施需求 頁面，有幾個重點要留意：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;啟動類型是 AWS Fargate 還是 Amazon EC2&amp;nbsp;&lt;/li&gt;&lt;li&gt;網路類型&lt;/li&gt;&lt;li&gt;作業系統/架構，CPU, 記憶體&lt;/li&gt;&lt;li&gt;任務角色，是否有從 Container 內發送 AWS API 的需求，以及執行 Container 建置時，要用哪個角色（預設為 ecsTaskExecutionRole）&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;此例先以 AWS Fargate, Linux/X86_64, 1 vCPU, 2GB 為例。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接下來定義容器的部分，此例會定義兩個容器，一個是 web container ，一個以 nginx 為基礎的 Docker Image，另一個是 app container，一個是 php-fpm 為基礎的 Docker Image&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;容器 - 1，一個單純的 Laravel framework project&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;- 名稱: php-fpm-docker&lt;/div&gt;&lt;div&gt;- 映像 URI: registry.gitlab.com/MyGroup/MyProject/myapp-php-fpm:latest&lt;/div&gt;&lt;div&gt;- 基本容器: 是&lt;/div&gt;&lt;div&gt;- 私有登入檔案: On&lt;/div&gt;&lt;div&gt;- Secrets Manager ARN 或名稱: arn:aws:secretsmanager:ap-northeast-1:####:secret:gitlab.com-container-registry&lt;/div&gt;&lt;div&gt;- 連接映射&lt;/div&gt;&lt;div&gt;&amp;nbsp; - 容器連接 9000, 通訊協定 TCP, 連接阜名稱 9000, 應用程式通訊協定 HTTP&lt;/div&gt;&lt;div&gt;- 唯讀根檔案系統, 唯讀: 否&lt;/div&gt;&lt;div&gt;- CPU: 0.5&lt;/div&gt;&lt;div&gt;- 記憶體硬性限制: 1.5GB, 記憶體軟性限制: 1.5GB&lt;/div&gt;&lt;div&gt;- 環境變數&lt;/div&gt;&lt;div&gt;&amp;nbsp; - PHPFPM_HOST, localhost&lt;/div&gt;&lt;div&gt;&amp;nbsp; - PHPFPM_PORT, 9000&lt;/div&gt;&lt;div&gt;- 運作狀態檢查: CMD-SHELL,/usr/local/bin/healthcheck.sh&lt;/div&gt;&lt;/blockquote&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLxaSi8D-rd55Ti4w7vsdPc8-mE5RdAnOEJQ9LCgbIxgtQAffghDKNIi8uboK14gpk3CkCFO6ltA_aSm6QUN1JrtFHJmA-OuIuMQXYrE08oZdnGh-Rf0X4u9WfiTW0-c8rD58e-rFi0zW4AS8UwsEZrGYdxZFWNIGJoJpZsl_AAdjnnRac-fh4-TIyWsbz/s1944/aws-esc-create-task-03-php-fpm-health-checker.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1202" data-original-width="1944" height="396" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLxaSi8D-rd55Ti4w7vsdPc8-mE5RdAnOEJQ9LCgbIxgtQAffghDKNIi8uboK14gpk3CkCFO6ltA_aSm6QUN1JrtFHJmA-OuIuMQXYrE08oZdnGh-Rf0X4u9WfiTW0-c8rD58e-rFi0zW4AS8UwsEZrGYdxZFWNIGJoJpZsl_AAdjnnRac-fh4-TIyWsbz/w640-h396/aws-esc-create-task-03-php-fpm-health-checker.png" width="640" /&gt;&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;容器 - 2，一個單純的 nginx web server&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;- 名稱: web-docker&lt;/div&gt;&lt;div&gt;- 映像 URI: registry.gitlab.com/MyGroup/MyProject/myapp-nginx:latest&lt;/div&gt;&lt;div&gt;- 基本容器: 是&lt;/div&gt;&lt;div&gt;- 私有登入檔案: On&lt;/div&gt;&lt;div&gt;- Secrets Manager ARN 或名稱: arn:aws:secretsmanager:ap-northeast-1:####:secret:gitlab.com-container-registry&lt;/div&gt;&lt;div&gt;- 連接映射&lt;/div&gt;&lt;div&gt;&amp;nbsp; - 容器連接 80, 通訊協定 TCP, 連接阜名稱 80, 應用程式通訊協定 HTTP&lt;/div&gt;&lt;div&gt;- 唯讀根檔案系統, 唯讀: 否&lt;/div&gt;&lt;div&gt;- CPU: 0.5&lt;/div&gt;&lt;div&gt;- 記憶體硬性限制: 0.5GB, 記憶體軟性限制: 0.5GB&lt;/div&gt;&lt;div&gt;- 環境變數&lt;/div&gt;&lt;div&gt;&amp;nbsp; - PHPFPM_HOST, localhost&lt;/div&gt;&lt;div&gt;&amp;nbsp; - PHPFPM_PORT, 9000&lt;/div&gt;&lt;div&gt;- 運作狀態檢查: CMD-SHELL,/usr/local/bin/healthcheck.sh&lt;/div&gt;&lt;div&gt;- 啟動相依性排序: php-fpm-docker, 條件: Healthy&lt;/div&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHzEcDRkYf9juymcqEUAE86R8L8rOeC1cfIEUhn7wCOrLy5fbDMPtgUH6BSZWHLQJgGDS5yO5zpB_G1EQP3RjaZNPZ_jQymPPIO0yfEIfyKcco2IHNK8pzB1MKfPzyW3ZiwZ9rIKo30NGqvQqPPGcIZB3IABGT7833meXiRJqTSjJGw3Z_Bpn7spjfP-Gl/s2666/aws-esc-create-task-04-nginx-depends-php-fpm.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1124" data-original-width="2666" height="270" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHzEcDRkYf9juymcqEUAE86R8L8rOeC1cfIEUhn7wCOrLy5fbDMPtgUH6BSZWHLQJgGDS5yO5zpB_G1EQP3RjaZNPZ_jQymPPIO0yfEIfyKcco2IHNK8pzB1MKfPzyW3ZiwZ9rIKo30NGqvQqPPGcIZB3IABGT7833meXiRJqTSjJGw3Z_Bpn7spjfP-Gl/w640-h270/aws-esc-create-task-04-nginx-depends-php-fpm.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;其中的 私有登入檔案 就是用來存取 registry.gitlab.com Container Registry 的地方，請記得在 Gitlab.com Project 建立 Deploy token 得到一組帳密：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyFoldTp3u2pW0wxioyfD9vHf6qjBUB-NaYXxHVTFLBpT9vg17MVx_vM_cFAEz8RrcUaLlgXWxe64-_wkz5Kp_CGuTD_p1Jdx06fKwuy2hc8chQ-KNAq1HsMSmOewUMdq7iN316VdYgYLmKJbexB5xl3DU4EyKndjCqdOzq4gfgKLct1a6OXyv0rHyGKPc/s2232/aws-esc-create-task-gitlab-deploy-token.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1820" data-original-width="2232" height="522" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyFoldTp3u2pW0wxioyfD9vHf6qjBUB-NaYXxHVTFLBpT9vg17MVx_vM_cFAEz8RrcUaLlgXWxe64-_wkz5Kp_CGuTD_p1Jdx06fKwuy2hc8chQ-KNAq1HsMSmOewUMdq7iN316VdYgYLmKJbexB5xl3DU4EyKndjCqdOzq4gfgKLct1a6OXyv0rHyGKPc/w640-h522/aws-esc-create-task-gitlab-deploy-token.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著到 AWS Secrets Manager 添加一組，在此定為 gitlab.com-container-registry 名稱:&lt;/div&gt;&lt;div&gt;&lt;span style="text-align: center;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="text-align: center;"&gt;https://ap-northeast-1.console.aws.amazon.com/secretsmanager/listsecrets?region=ap-northeast-1&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;div&gt;&lt;br style="text-align: start;" /&gt;&lt;/div&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIDm-03vLdg7TCDlSFjRC3lAmw3nxEUwlKlkxmP-2Dd8oSQ5mCyyMPtG-i5KjHhakOaMzvZM61W48OSa4MoTE99I8Acg4cJ7b6yrbJikJF1m5P_Hj2TfkVglPwfsKyvijnnN5SAtdfpR0u7dNg6ltliMIkS4I8uJtQchMYGhowRaczZ87VUz09kNXR7ifp/s2642/aws-esc-create-task-02-secretsmanager.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1618" data-original-width="2642" height="392" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIDm-03vLdg7TCDlSFjRC3lAmw3nxEUwlKlkxmP-2Dd8oSQ5mCyyMPtG-i5KjHhakOaMzvZM61W48OSa4MoTE99I8Acg4cJ7b6yrbJikJF1m5P_Hj2TfkVglPwfsKyvijnnN5SAtdfpR0u7dNg6ltliMIkS4I8uJtQchMYGhowRaczZ87VUz09kNXR7ifp/w640-h392/aws-esc-create-task-02-secretsmanager.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;添加完得到 arn:aws:secretsmanager:ap-northeast-1:####:secret:gitlab.com-container-registry。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;下一刻，到 AWS IAM Roles 設定，讓 ecsTaskExecutionRole 可以讀取 secretsmanager 內的資料:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;https://us-east-1.console.aws.amazon.com/iam/home?region=ap-northeast-1#/roles&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgscc_abS6Vam5mKPdLxXcahih8977swNhzeyv8Cx1LixtejQ2TmBSpPn5HMFoQNskNvYTioHj5oi1Vb7oOMfUfhtScrRxMAkQdGXcnQ20qlh7RBetzQLZjpjtivIAJjxUGcJb6l7xidOhRHmYc1hvJbQJHShlqvewWyYDtXDRU69R7VcnpmGoj05nb5c-/s1880/Screenshot%202025-02-12%20at%2016.16.29.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1398" data-original-width="1880" height="476" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgscc_abS6Vam5mKPdLxXcahih8977swNhzeyv8Cx1LixtejQ2TmBSpPn5HMFoQNskNvYTioHj5oi1Vb7oOMfUfhtScrRxMAkQdGXcnQ20qlh7RBetzQLZjpjtivIAJjxUGcJb6l7xidOhRHmYc1hvJbQJHShlqvewWyYDtXDRU69R7VcnpmGoj05nb5c-/w640-h476/Screenshot%202025-02-12%20at%2016.16.29.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFfZg0dLpiYumg9h_Bg69Fnp2WH0DYxp9PDmmmIxp7Z-mEIbCK-sxgwRXbbcIR82SpNtJIAo3tqo57gcP0sk3kkd01dysPy5W2WqVCnzblUlq7KRYeFQ_uzknEnjggmZom0ANI_aWsbwyeg_vQLMR3xTeGNUuq-066Npse3UitI_inCezHzRXoev0_dN2m/s2514/aws-ecs-iam-ecsTaskExecutionRole.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1320" data-original-width="2514" height="336" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFfZg0dLpiYumg9h_Bg69Fnp2WH0DYxp9PDmmmIxp7Z-mEIbCK-sxgwRXbbcIR82SpNtJIAo3tqo57gcP0sk3kkd01dysPy5W2WqVCnzblUlq7KRYeFQ_uzknEnjggmZom0ANI_aWsbwyeg_vQLMR3xTeGNUuq-066Npse3UitI_inCezHzRXoev0_dN2m/w640-h336/aws-ecs-iam-ecsTaskExecutionRole.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在此透過自訂一個足夠大的權限即可，例如可讀必要的資料即可（建議限制 Resource 可以存取地區甚至命名規則等）：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZc8SSvcZA2fuQfrzwZnM8IObAayfz5Pj-iv_TMQSmGvdoe0AYP-LEc3lVk3qNVwS6l-flEAm_uFIUGYpJJfrjMtsA7j5_6_jQgaz7IyGN-wkQvcE25NFzYqYq2zae-1YUxKUXXgQ6Tui24DQEY_Q4zI1cmfxHBUmlrIGOOBHZiuIonwXSIrikTHkdEGsL/s1894/aws-ecs-iam-ecsTaskExecutionRole-policy.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1254" data-original-width="1894" height="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZc8SSvcZA2fuQfrzwZnM8IObAayfz5Pj-iv_TMQSmGvdoe0AYP-LEc3lVk3qNVwS6l-flEAm_uFIUGYpJJfrjMtsA7j5_6_jQgaz7IyGN-wkQvcE25NFzYqYq2zae-1YUxKUXXgQ6Tui24DQEY_Q4zI1cmfxHBUmlrIGOOBHZiuIonwXSIrikTHkdEGsL/w640-h424/aws-ecs-iam-ecsTaskExecutionRole-policy.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如此，終於把 AWS ECS - Task 運行時所要的設定都安置好了，下一刻就是回到 AWS ECS - Cluster，建立一個 myapp-cluster，其中基礎設施維持使用 AWS Fargate (無伺服器) 模式，接著來建立 Service ，建立 AWS ECS -&amp;gt; Cluster -&amp;gt; Service 時，就比較多要留意的地方&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoDoFz8l00zqH80JLtqy8XfF_P3qAc7Y3dx9-M8PuuBrugaEaN7NuQrGbVtKaAewo-FuhLsTwUbheNODT5ueYTWtQU3koGDw3xoK4ABOxK9Sh6z1ogyTFnUxNr7jf4WF9pMgZzPMk5QntZxPxvSueE6c9FoqfW215-74Z3xQ0UG3J8S8iY2Ut9Nukivfkg/s2670/aws-esc-create-service-01.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1680" data-original-width="2670" height="402" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoDoFz8l00zqH80JLtqy8XfF_P3qAc7Y3dx9-M8PuuBrugaEaN7NuQrGbVtKaAewo-FuhLsTwUbheNODT5ueYTWtQU3koGDw3xoK4ABOxK9Sh6z1ogyTFnUxNr7jf4WF9pMgZzPMk5QntZxPxvSueE6c9FoqfW215-74Z3xQ0UG3J8S8iY2Ut9Nukivfkg/w640-h402/aws-esc-create-service-01.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;環境&lt;/li&gt;&lt;ul&gt;&lt;li&gt;運算組態: 都用預設的，需看一眼確認運算選項: 容量供應商策略&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;部署組態:&amp;nbsp;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;應用程式類型: 服務&lt;/li&gt;&lt;li&gt;任務定義: myapp-task (也就是剛剛創立的 myapp，內有定義兩個容器)&lt;/li&gt;&lt;li&gt;服務名稱: myapp-service&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;聯網:&amp;nbsp;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;VPC, 子網路: 需要留意, 可自行設計&lt;/li&gt;&lt;li&gt;安全群組: 需要留意, 可自行設計&lt;/li&gt;&lt;li&gt;公有 IP: 開啟&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Load Balancer&lt;/li&gt;&lt;ul&gt;&lt;li&gt;使用負載平衡&lt;/li&gt;&lt;li&gt;負載平衡器類型: Application Load Balancer&lt;/li&gt;&lt;li&gt;容器: web-docker 80:80&lt;/li&gt;&lt;li&gt;Application Load Balancer: 建立新的負載平衡器&lt;/li&gt;&lt;li&gt;負載平衡器名稱: myapp-service-load-balanacer&lt;/li&gt;&lt;li&gt;接聽程式: 80, HTTP&lt;/li&gt;&lt;li&gt;目標群組: 建立新的目標群組, 目標群組名稱 ecs-myapp--myapp-service, 通訊協定 HTTP, 取消註冊延遲 300, 運作狀態檢查通訊協定 HTTP, 運作狀態檢查路徑 /&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgymwy_K5MfbE7wAFunW_Yh6SZRwDqkm-YMUTsWW02uQpmDosNVqmuxGKkKhD0opgNhVFBV_wGCxJRAipcyoVnG0qkQW_YMLZFkF7AGYJFSE2U6NakOckR9z3Ov6ZkoRwf75V4i7NVRpgQeoOOLdB9S0JCtut9qDKSV1y_2Ae0HKk0zUMmTjQK9I9JKZGmQ/s2648/aws-esc-create-service-02-zerodowmtime.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1524" data-original-width="2648" height="368" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgymwy_K5MfbE7wAFunW_Yh6SZRwDqkm-YMUTsWW02uQpmDosNVqmuxGKkKhD0opgNhVFBV_wGCxJRAipcyoVnG0qkQW_YMLZFkF7AGYJFSE2U6NakOckR9z3Ov6ZkoRwf75V4i7NVRpgQeoOOLdB9S0JCtut9qDKSV1y_2Ae0HKk0zUMmTjQK9I9JKZGmQ/w640-h368/aws-esc-create-service-02-zerodowmtime.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2ycE62VfuyGdow6UO71zx7V9tZ_OIA9RJdOv9pc-Y1P_ugA3j0MhvPjn2kFQwnG4oJhMRkcke7qUKjfWPEskUWN_wEK3NK6UeKkh3EkKmxFPozFcYCsT0pRFn2uSojRRsGnLeHx61HGpQfK90qKIDY5xSOlaC_17o1UnUfFCaOmSV2umgzyWyNjmN-dSz/s2652/aws-esc-create-service-03-load-balancer.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1878" data-original-width="2652" height="454" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2ycE62VfuyGdow6UO71zx7V9tZ_OIA9RJdOv9pc-Y1P_ugA3j0MhvPjn2kFQwnG4oJhMRkcke7qUKjfWPEskUWN_wEK3NK6UeKkh3EkKmxFPozFcYCsT0pRFn2uSojRRsGnLeHx61HGpQfK90qKIDY5xSOlaC_17o1UnUfFCaOmSV2umgzyWyNjmN-dSz/w640-h454/aws-esc-create-service-03-load-balancer.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;按下建立 myapp-service 後，在 AWS ECS Cluster 頁面上不一定會馬上看到，有時需要重整頁面數次才會看到資訊，接著我們進去 Amazon Elastic Container Service -&amp;gt; 叢集 -&amp;gt; myapp-cluster -&amp;gt; 服務觀看：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;https://ap-northeast-1.console.aws.amazon.com/ecs/v2/clusters/myapp-cluster/services?region=ap-northeast-1&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1HrgXB-Z-di8EkpNinEHZg_XwUn60qpOUtc3SBwI3j2I8gr3GkCTi62K57hhqzHhY0nGS-PhZvIPTSxLxCUqjzn3nyX-NwZC7RjLfBHo4kPkiG74RexTiyjWtaJSyRx7xvPMlOFYAy4Aezju4W83jY8bFLfgzfstWfh3OFMC1d2gelBuHnB2mlR26m7Qt/s2576/aws-esc-create-service-04-create-service.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1448" data-original-width="2576" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1HrgXB-Z-di8EkpNinEHZg_XwUn60qpOUtc3SBwI3j2I8gr3GkCTi62K57hhqzHhY0nGS-PhZvIPTSxLxCUqjzn3nyX-NwZC7RjLfBHo4kPkiG74RexTiyjWtaJSyRx7xvPMlOFYAy4Aezju4W83jY8bFLfgzfstWfh3OFMC1d2gelBuHnB2mlR26m7Qt/w640-h360/aws-esc-create-service-04-create-service.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;再往下點進 myapp-service 進入到運作狀態，如網址 https://ap-northeast-1.console.aws.amazon.com/ecs/v2/clusters/myapp-cluster/services/myapp-service/health?region=ap-northeast-1 等&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;接著也可以點擊 Load Balancer 得知分配到的 DNS name，如 myapp-service-load-balanacer-##########.ap-northeast-1.elb.amazonaws.com 去瀏覽。此外 Load Balancer 這邊也有個小技巧，其實可以讓容器只提供 HTTP 80 服務，單純從 Load Balancer 增加 HTTPS 443 的服務，並把流量導過去 80，這也是讓 Load balancer 幫你扛掉一些算力的架構，但自身的服務也需要一些判斷設定，避免一直無限強制跳轉到 https 等。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如此，就算完成一個服務部署了，練習後想要刪光資源，記得刪掉 Service 後，有用到 Load balancer 的，還要特別去 EC2 Load Balancer 去刪除釋放掉資源，該 Load Balancer 就像一組 EC2 機器，創建多久算多久的錢。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接下來的實驗，就不需依賴 Load Balancer ，可以單純有個 Public IP 可存取即可。刪掉 Service 後，再重新創一個，接著我們要設計 CI/CD 架構，如何通報 AWS ECS 更新服務(Containers)，這邊研究一下後，有兩種設計原理:&lt;/div&gt;&lt;div&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;不停的去追蹤 registry.gitlab.com/MyGroup/MyProject/myapp-php-fpm:latest 和 registry.gitlab.com/MyGroup/MyProject/myapp-nginx:latest 是否有更新，有更新時透過 aws cli 通報 AWS ECS -&amp;gt; Cluster -&amp;gt; Service -&amp;gt; 觸發更新任務&lt;/li&gt;&lt;li&gt;在 Gitlab.com 創建 Docker images 時，最後一刻發動 aws cli 通報 AWS ECS -&amp;gt; Cluster -&amp;gt; Service -&amp;gt; 觸發更新任務&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;其中 (1) 的策略是透過 Amazon EventBridge 去排程執行，原理上需要記錄 Container Registry 的狀態，像是每次執行時，把資料記錄在 DB 內，下次檢查發現有變動時，呼叫 "AWS ECS -&amp;gt; Cluster -&amp;gt; Service -&amp;gt; 觸發更新任務" 後，再把狀態記錄下來，此優點是不用暴露 AWS 的資訊出去，缺點是效率較差，以及需要額外資料紀錄。此例 CI/CD 實踐是用 (2) 招式，建立一個身份可以完成 "AWS ECS -&amp;gt; Cluster -&amp;gt; Service -&amp;gt; 觸發更新任務" 即可。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;首先在 IAM 創立一個人員: gitlab.com-call-AWS-ECS-Cluster-Service-Update&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;https://us-east-1.console.aws.amazon.com/iam/home?region=ap-northeast-1#/users/create&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht_owWNufiARSGdvdBFRK9620ppWfjbmNGEPvegi5UeSSOqsBZ9Jv4goOtyHyCkzecinGphJC3x3vUoPzdhreLER89mS6BVcWAXpaJVp9gSEQVye3nniz7HREGmJ5FQu7MGGd2FssLLIfNuv7s7qNHbaOKI9BefpvAsN4wo9M3C9YEsJ2VS9RsQZd69atT/s1876/aws-cli-usage-iam-user-01.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1018" data-original-width="1876" height="348" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht_owWNufiARSGdvdBFRK9620ppWfjbmNGEPvegi5UeSSOqsBZ9Jv4goOtyHyCkzecinGphJC3x3vUoPzdhreLER89mS6BVcWAXpaJVp9gSEQVye3nniz7HREGmJ5FQu7MGGd2FssLLIfNuv7s7qNHbaOKI9BefpvAsN4wo9M3C9YEsJ2VS9RsQZd69atT/w640-h348/aws-cli-usage-iam-user-01.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVg-pFaUkawvCNBMFzJmIQw13lKvC-r2pVg3KjGHWE7KEuMLq3ADO-uCPxbOoC-SLmUrlNWrnDrZ58s3cmmdqzgGq6YSB-te9PKDjlgDWIsPSs6KepEZTaofb6S0XoyiAEOaPHMRluWpI9v7NvZB3GARUK0KjfMfDZGXRhxq74wSDDikWQMRs_9TNngpkX/s2502/aws-cli-usage-iam-user-02.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1378" data-original-width="2502" height="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVg-pFaUkawvCNBMFzJmIQw13lKvC-r2pVg3KjGHWE7KEuMLq3ADO-uCPxbOoC-SLmUrlNWrnDrZ58s3cmmdqzgGq6YSB-te9PKDjlgDWIsPSs6KepEZTaofb6S0XoyiAEOaPHMRluWpI9v7NvZB3GARUK0KjfMfDZGXRhxq74wSDDikWQMRs_9TNngpkX/w640-h352/aws-cli-usage-iam-user-02.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著建立政策: https://us-east-1.console.aws.amazon.com/iam/home?region=ap-northeast-1#/policies/create&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLT90n8VFLtwWvO4khIp65j0BMlEafp-YaRqoq78NTqP8faKHLjHEvT0iueMBrgLsriHhEmxp1qiXy1SDhfKmOlXjfRqhJLm_8d1nYBTN5pC0BGpFPYQfp331U24tTzRRocn7zqXaPz-VWkho7J3z3v9r-bhjBUqJHkTOrelr1TxmoDjS3X04VCZ1dN_Lc/s2506/aws-cli-usage-iam-user-03.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1378" data-original-width="2506" height="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLT90n8VFLtwWvO4khIp65j0BMlEafp-YaRqoq78NTqP8faKHLjHEvT0iueMBrgLsriHhEmxp1qiXy1SDhfKmOlXjfRqhJLm_8d1nYBTN5pC0BGpFPYQfp331U24tTzRRocn7zqXaPz-VWkho7J3z3v9r-bhjBUqJHkTOrelr1TxmoDjS3X04VCZ1dN_Lc/w640-h352/aws-cli-usage-iam-user-03.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIYmxVsbOk0igo0ZrPqr1vCaBawtjPw5aulvAtbQEwCiYi6zCCjiYII1TVvtU1P55iEWDaN2MTMBNo6TnBGVRmdTqfOGMigWnCNKN1v_DKajGVnbsVFdMxE_XBBnkb-B7GkzwivNa_OIMdtoIL_LicdiS1xjgQx8Qc2L_JNcgTOmNBuFV6ebmBdpK3ZezE/s1640/aws-cli-usage-iam-user-04.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="976" data-original-width="1640" height="380" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIYmxVsbOk0igo0ZrPqr1vCaBawtjPw5aulvAtbQEwCiYi6zCCjiYII1TVvtU1P55iEWDaN2MTMBNo6TnBGVRmdTqfOGMigWnCNKN1v_DKajGVnbsVFdMxE_XBBnkb-B7GkzwivNa_OIMdtoIL_LicdiS1xjgQx8Qc2L_JNcgTOmNBuFV6ebmBdpK3ZezE/w640-h380/aws-cli-usage-iam-user-04.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;給予以下權限&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;Read: DescribeServices&lt;/li&gt;&lt;li&gt;Write: UpdateService&lt;/li&gt;&lt;li&gt;Specify ARNs: ap-northeast-1, myapp-cluster&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;取名為: AWS-ECS-Cluster-Service-Update&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGPzjlrbFbqMjGYEXel8EYSoHrjerDmUTii1PHhhhBKH4LGLMk260MtZPYSfbZBz0Mgszg3HQt1B-2UiuJFsiX9TT_kehdh71VVPRcKHrxfqrnS2q9Jaaf_1dR6OW5sB-8ZQLs8rIfs5luE-tSBRIpqKix3RWrDClRel_MPf2QdjCqfww7OxvXF3ZaFFip/s2476/gitlab-cicd-settings.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1274" data-original-width="2476" height="330" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGPzjlrbFbqMjGYEXel8EYSoHrjerDmUTii1PHhhhBKH4LGLMk260MtZPYSfbZBz0Mgszg3HQt1B-2UiuJFsiX9TT_kehdh71VVPRcKHrxfqrnS2q9Jaaf_1dR6OW5sB-8ZQLs8rIfs5luE-tSBRIpqKix3RWrDClRel_MPf2QdjCqfww7OxvXF3ZaFFip/w640-h330/gitlab-cicd-settings.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如此，後續就可以改到 Gitlab.com 串 CI/CD 了，例如 .gitlab-ci.yml 和 &lt;a href="https://github.com/changyy/study-docker-container-registry-pipeline/blob/main/tool/aws/ecs-update-service.sh" rel="nofollow" target="_blank"&gt;tool/aws/ecs-update-service.sh&lt;/a&gt;：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;&lt;div style="background-color: white; color: #2e2e2e; font-family: &amp;quot;GitLab Mono&amp;quot;, &amp;quot;JetBrains Mono&amp;quot;, Menlo, &amp;quot;DejaVu Sans Mono&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, Consolas, &amp;quot;Ubuntu Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, &amp;quot;andale mono&amp;quot;, &amp;quot;lucida console&amp;quot;, monospace, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; line-height: 18px; white-space: pre;"&gt;&lt;div&gt;deploy_to_ecs:&lt;/div&gt;&lt;div&gt;  stage: &lt;span style="color: #dd1144;"&gt;deploy&lt;/span&gt;&lt;/div&gt;&lt;div&gt;  image: &lt;span style="color: #dd1144;"&gt;alpine:latest&lt;/span&gt;&lt;/div&gt;&lt;div&gt;  needs:&lt;/div&gt;&lt;div&gt;    - job: &lt;span style="color: #dd1144;"&gt;web_docker_image_build&lt;/span&gt;&lt;/div&gt;&lt;div&gt;      optional: &lt;span style="font-weight: bold;"&gt;true&lt;/span&gt;&lt;/div&gt;&lt;div&gt;    - job: &lt;span style="color: #dd1144;"&gt;app_docker_image_build&lt;/span&gt;&lt;/div&gt;&lt;div&gt;      optional: &lt;span style="font-weight: bold;"&gt;true&lt;/span&gt;&lt;/div&gt;&lt;div&gt;  before_script:&lt;/div&gt;&lt;div&gt;    - &lt;span style="color: #dd1144;"&gt;apk add --no-cache aws-cli curl jq tree bash&lt;/span&gt;&lt;/div&gt;&lt;div&gt;    - &lt;span style="color: #dd1144;"&gt;echo "AWS_ACCESS_KEY_ID = ${AWS_ACCESS_KEY_ID}"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;    - &lt;span style="color: #dd1144;"&gt;echo "AWS_SECRET_ACCESS_KEY = ${AWS_SECRET_ACCESS_KEY}"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;    - &lt;span style="color: #dd1144;"&gt;echo "AWS_DEFAULT_REGION = ${AWS_DEFAULT_REGION}"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;    - &lt;span style="color: #dd1144;"&gt;echo "ECS_DEPLOY_ENABLE = ${ECS_DEPLOY_ENABLE}" &lt;/span&gt;&lt;/div&gt;&lt;div&gt;    - &lt;span style="color: #dd1144;"&gt;echo "ECS_CLUSTER = ${ECS_CLUSTER}" &lt;/span&gt;&lt;/div&gt;&lt;div&gt;    - &lt;span style="color: #dd1144;"&gt;echo "ECS_SERVICE = ${ECS_SERVICE}" &lt;/span&gt;&lt;/div&gt;&lt;div&gt;  script:&lt;/div&gt;&lt;div&gt;    - &lt;span style="color: #dd1144;"&gt;chmod +x tool/aws/ecs-update-service.sh&lt;/span&gt;&lt;/div&gt;&lt;div&gt;    - &lt;span style="color: #dd1144;"&gt;bash tool/aws/ecs-update-service.sh&lt;/span&gt;&lt;/div&gt;&lt;div&gt;  rules:&lt;/div&gt;&lt;div&gt;    - if: &lt;span style="color: #dd1144;"&gt;$CI_COMMIT_TAG =~ /^web\-v\d+\.\d+\.\d+$/&lt;/span&gt;&lt;/div&gt;&lt;div&gt;      when: &lt;span style="color: #dd1144;"&gt;on_success&lt;/span&gt;&lt;/div&gt;&lt;div&gt;    - if: &lt;span style="color: #dd1144;"&gt;$CI_COMMIT_TAG =~ /^app\-v\d+\.\d+\.\d+$/ &lt;/span&gt;&lt;/div&gt;&lt;div&gt;      when: &lt;span style="color: #dd1144;"&gt;on_success&lt;/span&gt;&lt;/div&gt;&lt;div&gt;    - if: &lt;span style="color: #dd1144;"&gt;'$CI_PIPELINE_SOURCE == "web"'&lt;/span&gt;&lt;/div&gt;&lt;div&gt;      when: &lt;span style="color: #dd1144;"&gt;on_success&lt;/span&gt;&lt;/div&gt;&lt;div&gt;    - if: &lt;span style="color: #dd1144;"&gt;'$CI_PIPELINE_SOURCE == "api"'&lt;/span&gt;&lt;/div&gt;&lt;div&gt;      when: &lt;span style="color: #dd1144;"&gt;on_success&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;當創建了相關命名規則的 git tag 後，將觸發 deploy stage 做事，在此規劃 Gitlab.com Project -&amp;gt; Settings -&amp;gt; CI/CD 需要定義相關參數，當參數存在時，就會透過 AWS cli 更新雲端的服務，邏輯上依序呼叫：&lt;/div&gt;&lt;div&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;aws ecs describe-services --cluster myapp-cluster --services myapp-service | jq '.services | .[] | .events'&lt;/li&gt;&lt;li&gt;aws ecs wait services-stable --cluster myapp-cluster --services myapp-service &amp;amp;&amp;amp; echo "Status is stable"&lt;/li&gt;&lt;li&gt;aws ecs update-service --cluster myapp-cluster --service myapp-service --force-new-deployment&lt;/li&gt;&lt;li&gt;aws ecs wait services-stable --cluster myapp-cluster --services myapp-service &amp;amp;&amp;amp; echo "Done"&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;ref: &lt;a href="https://github.com/changyy/study-docker-container-registry-pipeline" rel="nofollow" target="_blank"&gt;github.com/changyy/study-docker-container-registry-pipeline&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;以上就是稍微完整的流程，但仍有很多有趣的地方沒有細說，包括 Docker 的建置、AWS VPC/Security Group/NAT Gateway/ECS Container 啟動類型的影響/Container 狀態檢查該怎樣設計/Zero Downtime/刪除 ECS 服務後仍要手動釋放Load Balancer等，此外，在 Gitlab.com 的 git tag 事件也有滿多有趣的，包括觸發 deploy 跟 build 的 git tag 規則是可以不一樣的，以及在 Gitlab.com 上的 Runner 運行了敏感資料 (aws cli,&amp;nbsp;AWS Access Key ID,&amp;nbsp;AWS Secret Access Key)、是否到底足夠信任挑選的 docker images 等等，都是值得深思的議題。&lt;/div&gt;</description><link>http://blog.changyy.org/2025/02/docker-aws-ecs-container-setup-gitlab-cicd-deploy.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidBrcDBSoB2B-BBeDfx8vLTh9kVLt0pm0CnYNbZ3ckDyIac9VPjd__Lgrm00AF83PoCx1mserYjqy0Pj1O0_RLuJoGCrmozCdyOzxd6BKOOAclIo1hB4zuoTvT-8puGUN2Q7MSd877bHSCYxmE02qJgDqLnf7XEp2rGfmdVMfbj8eNby3_tP2d7ckbrKO-/s72-w640-h204-c/gitlab-pipelines.png" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-2845086815362930276</guid><pubDate>Tue, 11 Feb 2025 17:14:00 +0000</pubDate><atom:updated>2025-02-16T21:25:01.265+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">aws</category><category domain="http://www.blogger.com/atom/ns#">build</category><category domain="http://www.blogger.com/atom/ns#">cicd</category><category domain="http://www.blogger.com/atom/ns#">deploy</category><category domain="http://www.blogger.com/atom/ns#">docker</category><category domain="http://www.blogger.com/atom/ns#">ecs</category><category domain="http://www.blogger.com/atom/ns#">ecsTaskExecutionRole</category><category domain="http://www.blogger.com/atom/ns#">gitlab</category><category domain="http://www.blogger.com/atom/ns#">IAM</category><category domain="http://www.blogger.com/atom/ns#">secretsmanager</category><title>Docker 開發筆記 - 從 Gitlab 觸發 CI/CD，製作 Docker Image 並發布到 Amazon Elastic Container Service (AWS ECS)</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo4h3VUFqfRLo-YuNSfpTIlhqF4Z53cMKvG1rMP0SZNKp_F8VgTU9C43tsuvlQ4mpxfMnD8Fxs1EfLrn9gx5R9Q5QmZUwzMQEdXSI9f8liZlyD0m_dM9EPUYw_mQBR_SMzITTDuP-4ayEMtQwGlNrMnglsy6Zl11nGl6SJSCe6_tyr6qTWUnp-Jxn3GYeC/s2494/gitlab-build-pipeline.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1402" data-original-width="2494" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo4h3VUFqfRLo-YuNSfpTIlhqF4Z53cMKvG1rMP0SZNKp_F8VgTU9C43tsuvlQ4mpxfMnD8Fxs1EfLrn9gx5R9Q5QmZUwzMQEdXSI9f8liZlyD0m_dM9EPUYw_mQBR_SMzITTDuP-4ayEMtQwGlNrMnglsy6Zl11nGl6SJSCe6_tyr6qTWUnp-Jxn3GYeC/w640-h360/gitlab-build-pipeline.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;以前常做的事是自行弄個 Jenkins 在搭配 Ansible 連續技完成 CI/CD 的任務，今年要來推廣 Docker 生態，把簡單的任務包裝成 Docker 降低人員維護的成本，在此挑選 Gitlab.com 作為出發點，接著會使用 Gitlab CI/CD 發布到 Amazon Elastic Container Service。這篇筆記不限於 Laravel framework ，但以他當作範例來進行。&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;Deploy to Amazon Elastic Container Service&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://docs.gitlab.com/ee/ci/cloud_deployment/ecs/deploy_to_aws_ecs.html" rel="nofollow" target="_blank"&gt;https://docs.gitlab.com/ee/ci/cloud_deployment/ecs/deploy_to_aws_ecs.html&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Deploy to AWS from GitLab CI/CD&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://docs.gitlab.com/ee/ci/cloud_deployment/" rel="nofollow" target="_blank"&gt;https://docs.gitlab.com/ee/ci/cloud_deployment/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;首先就是弄個 gitlab project，可以是 Create blank project 也可以是 Create from template，接著就到 Settings -&amp;gt; CI/CD 欣賞一下有哪些設定選項&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;General pipelines&lt;/div&gt;&lt;div&gt;Auto DevOps&lt;/div&gt;&lt;div&gt;Runners&lt;/div&gt;&lt;div&gt;Artifacts&lt;/div&gt;&lt;div&gt;Variables&lt;/div&gt;&lt;div&gt;Pipeline trigger tokens&lt;/div&gt;&lt;div&gt;Deploy freezes&lt;/div&gt;&lt;div&gt;Job token permissions&lt;/div&gt;&lt;div&gt;Secure files&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在 General pipelines 介面上，可以看到滿多設定的，但整體上起源是在 "Deploy to AWS from GitLab CI/CD" 這篇文章中，整個精髓是在 .gitlab-ci.yml 檔案上，因此可以參考 "Create from template" 的素材，當下可以按 preview 先欣賞一下，多參考幾個就有感覺了：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;rails: &lt;a href="https://gitlab.com/gitlab-org/project-templates/rails" rel="nofollow" target="_blank"&gt;https://gitlab.com/gitlab-org/project-templates/rails&lt;/a&gt;&lt;/li&gt;&lt;li&gt;react: &lt;a href="https://gitlab.com/gitlab-org/project-templates/react" rel="nofollow" target="_blank"&gt;https://gitlab.com/gitlab-org/project-templates/react&lt;/a&gt;&lt;/li&gt;&lt;li&gt;hugo: &lt;a href="https://gitlab.com/pages/hugo" rel="nofollow" target="_blank"&gt;https://gitlab.com/pages/hugo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;laravel: &lt;a href="https://gitlab.com/gitlab-org/project-templates/laravel" rel="nofollow" target="_blank"&gt;https://gitlab.com/gitlab-org/project-templates/laravel&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;簡言之 .gitlab-ci.yml 就是定義想做的指令，並且可以指定要用哪個環境(docker image)來運行這些指令。例如 Laravel 範例使用的 image 是 edbizarro/gitlab-ci-pipeline-php:latest 位置，對應的就是在 https://hub.docker.com/r/edbizarro/gitlab-ci-pipeline-php 和 https://github.com/edbizarro/gitlab-ci-pipeline-php ，搞懂後就來建立自己的 .gitlab-ci.yml 檔案，目標是直接依照專案內的 Dockerfile 來編譯 Docker Images：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;variables:&lt;/div&gt;&lt;div&gt;&amp;nbsp; DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT: "myapp-php-fpm"&lt;/div&gt;&lt;div&gt;&amp;nbsp; DOCKER_IMAGE_NAME_FOR_NGINX: "myapp-nginx"&lt;/div&gt;&lt;div&gt;&amp;nbsp; DOCKER_TLS_CERTDIR: "/certs"&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;stages:&lt;/div&gt;&lt;div&gt;&amp;nbsp; - build&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;build:&lt;/div&gt;&lt;div&gt;&amp;nbsp; stage: build&lt;/div&gt;&lt;div&gt;&amp;nbsp; image: docker:latest&lt;/div&gt;&lt;div&gt;&amp;nbsp; services:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker:dind&lt;/div&gt;&lt;div&gt;&amp;nbsp; script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - ls -la $DOCKER_TLS_CERTDIR&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker info&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker build -t $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT:latest -f docker/php-fpm/Dockerfile .&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker build -t $DOCKER_IMAGE_NAME_FOR_NGINX:latest -f docker/nginx/Dockerfile .&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker images&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - pwd&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - ls -la&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker save $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT:latest &amp;gt; $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT.image.tar&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker save $DOCKER_IMAGE_NAME_FOR_NGINX:latest &amp;gt; $DOCKER_IMAGE_NAME_FOR_NGINX.image.tar&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - ls -la&lt;/div&gt;&lt;div&gt;&amp;nbsp; artifacts:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; paths:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT.image.tar&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - $DOCKER_IMAGE_NAME_FOR_NGINX.image.tar&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; expire_in: 1 week&lt;/div&gt;&lt;div&gt;&amp;nbsp; rules:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - changes:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - Dockerfile&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - docker/**/*&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "web"'&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "api"'&lt;/div&gt;&lt;div&gt;&amp;nbsp; #tags:&lt;/div&gt;&lt;div&gt;&amp;nbsp; #&amp;nbsp; - docker&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如此，回到 Gitlab Project 左邊的 Build 項目，就可以在 Pipelines 去觸發 New pipeline 來觸發 Build docker images ，並且會把產出的 *.image.tar 壓縮起來供下載。此外，在 Build 內的 Pipeline editor 也是滿好用的架構，可以線上編輯 .gitlab-ci.yml 檔案內容，還可以驗證格式內容是否有錯誤。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接下來又做更多事，像是 Laravel framework 相關專案設定和初始化（包括 npm install &amp;amp;&amp;amp; npm run build &amp;amp;&amp;amp; php artisan optimize）:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;variables:&lt;/div&gt;&lt;div&gt;&amp;nbsp; DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT: "myapp-php-fpm"&lt;/div&gt;&lt;div&gt;&amp;nbsp; DOCKER_IMAGE_NAME_FOR_NGINX: "myapp-nginx"&lt;/div&gt;&lt;div&gt;&amp;nbsp; DOCKER_TLS_CERTDIR: "/certs"&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;stages:&lt;/div&gt;&lt;div&gt;&amp;nbsp; - prepare&lt;/div&gt;&lt;div&gt;&amp;nbsp; - build&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;composer:&lt;/div&gt;&lt;div&gt;&amp;nbsp; stage: prepare&lt;/div&gt;&lt;div&gt;&amp;nbsp; image: php:8.4-cli&lt;/div&gt;&lt;div&gt;&amp;nbsp; before_script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - apt-get update &amp;amp;&amp;amp; apt-get install -y unzip libzip-dev&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker-php-ext-install zip&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer&lt;/div&gt;&lt;div&gt;&amp;nbsp; script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # https://gitlab.com/gitlab-org/project-templates/laravel/-/blob/main/.gitlab-ci.yml?ref_type=heads&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - cd www&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - composer install --no-dev --optimize-autoloader&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; #- php artisan optimize:clear&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - php artisan optimize&lt;/div&gt;&lt;div&gt;&amp;nbsp; artifacts:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; paths:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - www/&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; expire_in: 1 day&lt;/div&gt;&lt;div&gt;&amp;nbsp; rules:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - changes:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - www/**/*&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "web"'&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "api"'&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;npm:&lt;/div&gt;&lt;div&gt;&amp;nbsp; stage: prepare&lt;/div&gt;&lt;div&gt;&amp;nbsp; image: node:22&lt;/div&gt;&lt;div&gt;&amp;nbsp; dependencies:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - composer&lt;/div&gt;&lt;div&gt;&amp;nbsp; script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - cd www&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - npm install&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - npm run build&lt;/div&gt;&lt;div&gt;&amp;nbsp; artifacts:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; paths:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - www/node_modules/&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - www/public/build/&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; expire_in: 1 day&lt;/div&gt;&lt;div&gt;&amp;nbsp; rules:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - changes:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - www/**/*&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "web"'&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "api"'&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;build:&lt;/div&gt;&lt;div&gt;&amp;nbsp; stage: build&lt;/div&gt;&lt;div&gt;&amp;nbsp; image: docker:latest&lt;/div&gt;&lt;div&gt;&amp;nbsp; services:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker:dind&lt;/div&gt;&lt;div&gt;&amp;nbsp; dependencies:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - composer&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - npm&lt;/div&gt;&lt;div&gt;&amp;nbsp; script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker info&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker build -t $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT:latest -f docker/php-fpm/Dockerfile .&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker build -t $DOCKER_IMAGE_NAME_FOR_NGINX:latest -f docker/nginx/Dockerfile .&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker save $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT:latest &amp;gt; $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT.image.tar&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker save $DOCKER_IMAGE_NAME_FOR_NGINX:latest &amp;gt; $DOCKER_IMAGE_NAME_FOR_NGINX.image.tar&lt;/div&gt;&lt;div&gt;&amp;nbsp; artifacts:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; paths:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT.image.tar&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - $DOCKER_IMAGE_NAME_FOR_NGINX.image.tar&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; expire_in: 1 week&lt;/div&gt;&lt;div&gt;&amp;nbsp; rules:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - changes:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - Dockerfile&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - docker/**/*&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - www/**/*&amp;nbsp; # 加入 www 目錄的變更觸發&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "web"'&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "api"'&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;最後，再來增加把 Docker images 發布到 AWS ECS ，目前應當有兩招可以發布：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;使用 Gitlab Container Registry&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://docs.gitlab.com/ee/user/packages/container_registry/index.html" rel="nofollow" target="_blank"&gt;https://docs.gitlab.com/ee/user/packages/container_registry/index.html&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;使用 AWS ECS Private Registry&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/private-auth.html" rel="nofollow" target="_blank"&gt;https://docs.aws.amazon.com/AmazonECS/latest/developerguide/private-auth.html&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;先試試看 Gitlab Container Registry ，過程就是在 Build stage 中，增加登入到 Gitlab Container Registry 機制:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;before_script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" "$CI_REGISTRY" --password-stdin&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;實際 Job 運行會看到這段資訊：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;$ echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" "$CI_REGISTRY" --password-stdin&lt;/div&gt;&lt;div&gt;WARNING! Your password will be stored unencrypted in /root/.docker/config.json.&lt;/div&gt;&lt;div&gt;Configure a credential helper to remove this warning. See&lt;/div&gt;&lt;div&gt;https://docs.docker.com/engine/reference/commandline/login/#credential-stores&lt;/div&gt;&lt;div&gt;Login Succeeded&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著再把這些串起來，在 build stage 時，就把產生的 docker image 送進 Gitlab Container Registry，並且在 Gitlab Project -&amp;gt; Deploy -&amp;gt; Container Registry 看到成果：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;variables:&lt;/div&gt;&lt;div&gt;&amp;nbsp; DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT: "myapp-php-fpm"&lt;/div&gt;&lt;div&gt;&amp;nbsp; DOCKER_IMAGE_NAME_FOR_NGINX: "myapp-nginx"&lt;/div&gt;&lt;div&gt;&amp;nbsp; DOCKER_TLS_CERTDIR: "/certs"&lt;/div&gt;&lt;div&gt;&amp;nbsp; PHP_IMAGE_PATH: "$CI_REGISTRY_IMAGE/$DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT"&lt;/div&gt;&lt;div&gt;&amp;nbsp; NGINX_IMAGE_PATH: "$CI_REGISTRY_IMAGE/$DOCKER_IMAGE_NAME_FOR_NGINX"&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;stages:&lt;/div&gt;&lt;div&gt;&amp;nbsp; - prepare&lt;/div&gt;&lt;div&gt;&amp;nbsp; - build&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;composer:&lt;/div&gt;&lt;div&gt;&amp;nbsp; stage: prepare&lt;/div&gt;&lt;div&gt;&amp;nbsp; image: php:8.4-cli&lt;/div&gt;&lt;div&gt;&amp;nbsp; before_script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - apt-get update &amp;amp;&amp;amp; apt-get install -y unzip libzip-dev&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker-php-ext-install zip&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer&lt;/div&gt;&lt;div&gt;&amp;nbsp; script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # https://gitlab.com/gitlab-org/project-templates/laravel/-/blob/main/.gitlab-ci.yml?ref_type=heads&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - cd www&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - composer install --no-dev --optimize-autoloader&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; #- php artisan optimize:clear&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - php artisan optimize&lt;/div&gt;&lt;div&gt;&amp;nbsp; artifacts:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; paths:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - www/&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; expire_in: 1 day&lt;/div&gt;&lt;div&gt;&amp;nbsp; rules:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - changes:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - www/**/*&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "web"'&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "api"'&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;npm:&lt;/div&gt;&lt;div&gt;&amp;nbsp; stage: prepare&lt;/div&gt;&lt;div&gt;&amp;nbsp; image: node:22&lt;/div&gt;&lt;div&gt;&amp;nbsp; dependencies:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - composer&lt;/div&gt;&lt;div&gt;&amp;nbsp; script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - cd www&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - npm install&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - npm run build&lt;/div&gt;&lt;div&gt;&amp;nbsp; artifacts:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; paths:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - www/node_modules/&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - www/public/build/&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; expire_in: 1 day&lt;/div&gt;&lt;div&gt;&amp;nbsp; rules:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - changes:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - www/**/*&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "web"'&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "api"'&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;build:&lt;/div&gt;&lt;div&gt;&amp;nbsp; stage: build&lt;/div&gt;&lt;div&gt;&amp;nbsp; image: docker:latest&lt;/div&gt;&lt;div&gt;&amp;nbsp; services:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker:dind&lt;/div&gt;&lt;div&gt;&amp;nbsp; dependencies:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - composer&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - npm&lt;/div&gt;&lt;div&gt;&amp;nbsp; before_script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" "$CI_REGISTRY" --password-stdin&lt;/div&gt;&lt;div&gt;&amp;nbsp; script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker info&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker build -t $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT:latest -f docker/php-fpm/Dockerfile .&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker build -t $DOCKER_IMAGE_NAME_FOR_NGINX:latest -f docker/nginx/Dockerfile .&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker save $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT:latest &amp;gt; $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT.image.tar&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker save $DOCKER_IMAGE_NAME_FOR_NGINX:latest &amp;gt; $DOCKER_IMAGE_NAME_FOR_NGINX.image.tar&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker tag $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT:latest $PHP_IMAGE_PATH:$CI_COMMIT_SHA&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker tag $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT:latest $PHP_IMAGE_PATH:latest&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker tag $DOCKER_IMAGE_NAME_FOR_NGINX:latest $NGINX_IMAGE_PATH:$CI_COMMIT_SHA&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker tag $DOCKER_IMAGE_NAME_FOR_NGINX:latest $NGINX_IMAGE_PATH:latest&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker push $PHP_IMAGE_PATH:$CI_COMMIT_SHA&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker push $PHP_IMAGE_PATH:latest&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker push $NGINX_IMAGE_PATH:$CI_COMMIT_SHA&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker push $NGINX_IMAGE_PATH:latest&lt;/div&gt;&lt;div&gt;&amp;nbsp; artifacts:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; paths:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - $DOCKER_IMAGE_NAME_FOR_LARAVEL_PROJECT.image.tar&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - $DOCKER_IMAGE_NAME_FOR_NGINX.image.tar&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; expire_in: 1 week&lt;/div&gt;&lt;div&gt;&amp;nbsp; rules:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - changes:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - Dockerfile&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - docker/**/*&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - www/**/*&amp;nbsp; # 加入 www 目錄的變更觸發&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "web"'&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - if: '$CI_PIPELINE_SOURCE == "api"'&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如此就只需要到 AWS ECS 後台去建立 Task 改從 Gitlab Container Registry 取資料即可完成發佈，這個架構的優勢是避免在 Gitlab.com 關聯太多 AWS 存取方式，也可以避免 AWS 相關資訊外洩，若要更加保護的話，可以連 Gitlab CI/CD 的 Runner 都挑自己的機器會更穩。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmgZIX9DnjB24Q25fKb-jXHdOKOb1tyysrqJqcifPOil2hzIZwvjfe9z9Br_iMBkEdhSgy1BsZo1G310tEjlG4TS-a1qMjaNQU002g3GLnRvtdLS7htoaoiMUPE0hhZ8AOdHdU9vDeFvaSLJlmboN2c0PiyvMj6_nHeGLZrBxL6wDxGKyfMK_PvoaUWsfr/s2502/gitlab-quota.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1326" data-original-width="2502" height="340" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmgZIX9DnjB24Q25fKb-jXHdOKOb1tyysrqJqcifPOil2hzIZwvjfe9z9Br_iMBkEdhSgy1BsZo1G310tEjlG4TS-a1qMjaNQU002g3GLnRvtdLS7htoaoiMUPE0hhZ8AOdHdU9vDeFvaSLJlmboN2c0PiyvMj6_nHeGLZrBxL6wDxGKyfMK_PvoaUWsfr/w640-h340/gitlab-quota.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;最後，還有一點要留意的，對 Gitlab.com 免費服務有 5GB 空間的限制，假設 Docker Image 隨便就佔了幾百GB，那發佈幾次後就會沒空間了，因此，再補上一段只保留最後三版 Docker Image 的機制：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;cleanup_images:&lt;/div&gt;&lt;div&gt;&amp;nbsp; stage: cleanup&lt;/div&gt;&lt;div&gt;&amp;nbsp; image: docker:latest&lt;/div&gt;&lt;div&gt;&amp;nbsp; variables:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; GIT_STRATEGY: none&lt;/div&gt;&lt;div&gt;&amp;nbsp; before_script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - apk add --no-cache curl jq&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - echo "CI_API_V4_URL = ${CI_API_V4_URL}"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - echo "CI_PROJECT_ID = ${CI_PROJECT_ID}"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # https://docs.gitlab.com/ee/api/container_registry.html&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - echo "CI_REGISTRY_PASSWORD = ${CI_REGISTRY_PASSWORD}"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - echo "CI_JOB_TOKEN = ${CI_JOB_TOKEN}"&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - echo "API REQUEST URL = ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/registry/repositories"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - |&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/registry/repositories" | jq '.'&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; script:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - |&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; REGISTRY_TARGET=$(curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/registry/repositories" | jq -r '. | if length &amp;gt; 0 then .[].id else empty end')&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; for REGISTRY_ID in $REGISTRY_TARGET; do&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo "REGISTRY_ID = $REGISTRY_ID"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/registry/repositories/${REGISTRY_ID}/tags"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; REGISTRY_LAST_TAG=$(curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/registry/repositories/${REGISTRY_ID}/tags" | jq -r '[ .[] | select(.name != "latest") ] | sort_by(.name) | if length &amp;gt; 3 then .[:-3] | .[].name else empty end')&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for REGISTRY_TAG in $REGISTRY_LAST_TAG; do&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo "REGISTRY_ID = $REGISTRY_ID, REGISTRY_TAG = $REGISTRY_TAG"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; curl --request DELETE&amp;nbsp; --header "JOB-TOKEN: ${CI_JOB_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/registry/repositories/${REGISTRY_ID}/tags/${REGISTRY_TAG}"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; done&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; done&lt;/div&gt;&lt;div&gt;&amp;nbsp; rules:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; - when: on_success&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;主要是透過 CI_JOB_TOKEN 跟 Gitlab API 溝通，像是取出該專案下有幾個 Container Registry ，並且善用 docker tag 時，把 tag prefix 增加 timestamp ，方便此時 sorting 並挑選舊的 tag 且移除他們，如此就可以節省 Gitlab Project 使用空間。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;至於在 AWS ECS 的設定，就主要先看 Gitlab 跟 AWS ECS 官方文件了，由於 Docker Image 我們擺在 Gitlab.com，這時 AWS ECS -&amp;gt; Cluster -&amp;gt; Task ，在定義 Container 要從哪邊 Container Registry 取出時，要寫入&amp;nbsp;registry.gitlab.com/YourGitlabGroup/YourGitlabProject/YourContainerRegistryProject:latest 等類似的位置，下一刻則是 AWS ECS 運行時，此時 ecsTaskExecutionRole 怎麼存取的議題&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;流程：&lt;/div&gt;&lt;div&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;在 Gitlab Project 上，先建立一個 Deploy Token，會拿到一個帳號跟密碼&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Settings -&amp;gt; Repository -&amp;gt; Deploy tokens -&amp;gt; Add token&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;在 AWS Secrets Manager 上，添加一組，選擇其他類型項目即可&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhx2YZ_SKxf1o9LKCVBGF7trW5PZB6goiYpi_Z7nWsbdZnViAmztQsm_4fiE70Np8CaowqkZz2QRIO6ngBlT-WUy4eeH2rMGfpSOOxwxVWwvxuzA_BINiHfFLMcu_T0rScTi4vYTi6EZwh1yfyx7enmzCRj-CujD5KVSWTAai3pptmdRFy68tEnJbBM4RM5/s1024/secretsmanager.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="811" data-original-width="1024" height="316" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhx2YZ_SKxf1o9LKCVBGF7trW5PZB6goiYpi_Z7nWsbdZnViAmztQsm_4fiE70Np8CaowqkZz2QRIO6ngBlT-WUy4eeH2rMGfpSOOxwxVWwvxuzA_BINiHfFLMcu_T0rScTi4vYTi6EZwh1yfyx7enmzCRj-CujD5KVSWTAai3pptmdRFy68tEnJbBM4RM5/w400-h316/secretsmanager.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;假設產出是 arn:aws:secretsmanager:ap-northeast-1:#####:secret:gitlab.com-container-registry&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;在 AWS&amp;nbsp;Identity and Access Management (IAM) 上，找到 ecsTaskExecutionRole ，幫它添加可以存取 Secrets Manager 的權限&lt;/li&gt;&lt;ul&gt;&lt;li&gt;開放權限可以只提供 READOnly ，甚至細微到只能對 arn:aws:secretsmanager:ap-northeast-1:#####:secret:gitlab.com-container-registry 操作&lt;/li&gt;&lt;li&gt;沒設定好的錯誤訊息：&lt;/li&gt;&lt;ul&gt;&lt;li&gt;ResourceInitializationError: unable to pull secrets or registry auth: execution resource retrieval failed: unable to get registry auth from asm: service call has been retried 1 time(s): failed to fetch secret arn:aws:secretsmanager:ap-northeast-1:#####:secret:gitlab.com-container-registry from secrets manager: AccessDeniedException: User: arn:aws:sts::#####:assumed-role/ecsTaskExecutionRole/##### is not authorized to perform: secretsmanager:GetSecretValue on resource: arn:aws:secretsmanager:ap-northeast-1:#####:secret:gitlab.com-container-registry because no identity-based policy allows the secretsmanager:GetSecretValue action status code: 400&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;設定範例 JSON&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;div&gt;{&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "Version": "2012-10-17",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "Statement": [&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "Sid": "VisualEditor0",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "Effect": "Allow",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "Action": [&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "secretsmanager:GetRandomPassword",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "secretsmanager:GetResourcePolicy",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "secretsmanager:GetSecretValue",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "secretsmanager:DescribeSecret",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "secretsmanager:ListSecretVersionIds",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "secretsmanager:ListSecrets",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "secretsmanager:BatchGetSecretValue"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ],&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; "Resource": "*"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; ]&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;li&gt;在 AWS ECS 的 Task 就可以好好設定 Container 的存取機制&lt;/li&gt;&lt;ul&gt;&lt;li&gt;私有登錄檔 -&amp;gt; Secrets Manager ARN 或名稱 -&amp;gt; arn:aws:secretsmanager:ap-northeast-1:#####:secret:gitlab.com-container-registry&lt;/li&gt;&lt;/ul&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;如此在 AWS ECS -&amp;gt; Cluster -&amp;gt; Create Service 時，就會觸發 AWS ECS Task 運行，就可以看到 Docker Container 被創立出來&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;目前研究先告一個段落，在 AWS ECS 的設置規劃其實也要不小的篇幅的。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;再碎碎念一下，直接實驗 AWS ECS 直接去跟 Gitlab.com Container Registry 要資料時，有時會出現彷彿無法正常取出的問題，這個我還不是很清楚排除的方式，目前還有偷懶隔天再弄一次，一樣的方式又正常可通過 Orz 或許最佳還是改用 AWS ECS Private Container Registry 維護，這樣服務發布架構可以省去一些不穩定的額外煩惱。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;類似錯誤訊息：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;esourceInitializationError: unable to pull secrets or registry auth: unable to get registry auth from asm: There is a connection issue between the task and AWS Secrets Manager.&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;數個資安提問：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;假設你的 Docker Images 是在 Gitlab.com 產出，要 docker push 到 AWS ECS Container Registry 時，就會要面對在 Gitlab.com 上暴露 AWS 存取的資訊&lt;/li&gt;&lt;li&gt;假設 Build docker images 是用 Gitlab.com 機器(Runner)，對應的也是有敏感資訊會在這些機器上，你是否信任他們？&lt;/li&gt;&lt;li&gt;假設 Build docker images 時，用了其他人製作的 Docker images ，你的敏感資訊是否有妥善保護？惡意的 docker images 可以在啟動或結束時，把用戶的敏感資訊傳遞出去&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;ref:&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;&lt;a href="https://github.com/changyy/study-docker-container-registry-pipeline" rel="nofollow" target="_blank"&gt;github.com/changyy/study-docker-container-registry-pipeline&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://blog.changyy.org/2025/02/docker-aws-ecs-container-setup-gitlab-cicd-deploy.html" rel="nofollow" target="_blank"&gt;Docker 開發筆記 - AWS ECS 發布 Docker Container 服務和 Gitlab CI/CD 架構實踐&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</description><link>http://blog.changyy.org/2025/02/docker-gitlab-cicd-docker-image-amazon.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo4h3VUFqfRLo-YuNSfpTIlhqF4Z53cMKvG1rMP0SZNKp_F8VgTU9C43tsuvlQ4mpxfMnD8Fxs1EfLrn9gx5R9Q5QmZUwzMQEdXSI9f8liZlyD0m_dM9EPUYw_mQBR_SMzITTDuP-4ayEMtQwGlNrMnglsy6Zl11nGl6SJSCe6_tyr6qTWUnp-Jxn3GYeC/s72-w640-h360-c/gitlab-build-pipeline.png" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-5152121810824271791</guid><pubDate>Tue, 17 Dec 2024 12:13:00 +0000</pubDate><atom:updated>2024-12-17T20:15:10.271+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Electron</category><category domain="http://www.blogger.com/atom/ns#">electron-vite</category><category domain="http://www.blogger.com/atom/ns#">mDNS</category><category domain="http://www.blogger.com/atom/ns#">node.js</category><category domain="http://www.blogger.com/atom/ns#">vite</category><category domain="http://www.blogger.com/atom/ns#">vue</category><title>Node.js 開發筆記 - 使用 Electron + Vite + Vue 開發一款 PC app ，可支援 mDNS 裝置搜尋 @ macOS 15.2</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqrmKH3EtBH1ato1hlevc5Y2qbHSaX60ocFR3TEZTdFo0ncp03ZeeifezLDiLrqTsx_k_6QUmBtK7HmpsW3yOFtMJ-kumvTw9z1iCNKJ1ge0_sHe1WIgfFlNk7yRyWXGTYAEuU23GEbHKHkChP4CPvd6JjN6TrfBX58bcKp-MpF52d3KaNUneCQE1SH3yx/s1588/Screenshot%202024-12-17%20at%2020.00.00.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1190" data-original-width="1588" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqrmKH3EtBH1ato1hlevc5Y2qbHSaX60ocFR3TEZTdFo0ncp03ZeeifezLDiLrqTsx_k_6QUmBtK7HmpsW3yOFtMJ-kumvTw9z1iCNKJ1ge0_sHe1WIgfFlNk7yRyWXGTYAEuU23GEbHKHkChP4CPvd6JjN6TrfBX58bcKp-MpF52d3KaNUneCQE1SH3yx/w640-h480/Screenshot%202024-12-17%20at%2020.00.00.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;有點久沒用 Electron 開發 PC app ，協助同事從旁實驗一下新的架構，這次研究後，直接用 electron-vite 初始化專案，核心就是 Electron + Vite 的研發環境，前台是 Vue 前端畫面。原先在想是不是一律自行安裝套件且故意都裝最新版，想完後還是縮一下，回歸別人整理好的工具，而這次強調用 Vite 整合，就不從 electron-vue 開始。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;開發環境：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% sw_vers&amp;nbsp;&lt;/div&gt;&lt;div&gt;ProductName:&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;macOS&lt;/div&gt;&lt;div&gt;ProductVersion:&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;15.2&lt;/div&gt;&lt;div&gt;BuildVersion:&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;24C101&lt;/div&gt;&lt;div&gt;% nvm use --lts&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;Now using node v22.12.0 (npm v10.9.0)&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;流水帳：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% npm create electron-vite@latest&lt;/div&gt;&lt;div&gt;Need to install the following packages:&lt;/div&gt;&lt;div&gt;create-electron-vite@0.7.1&lt;/div&gt;&lt;div&gt;Ok to proceed? (y) y&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;gt; npx&lt;/div&gt;&lt;div&gt;&amp;gt; create-electron-vite&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;✔ Project name: … my-electron-app&lt;/div&gt;&lt;div&gt;✔ Project template: › Vue&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Scaffolding project in /private/tmp/my-electron-app...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Done. Now run:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; cd my-electron-app&lt;/div&gt;&lt;div&gt;&amp;nbsp; npm install&lt;/div&gt;&lt;div&gt;&amp;nbsp; npm run dev&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;npm notice&lt;/div&gt;&lt;div&gt;npm notice New major version of npm available! 10.9.0 -&amp;gt; 11.0.0&lt;/div&gt;&lt;div&gt;npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.0.0&lt;/div&gt;&lt;div&gt;npm notice To update run: npm install -g npm@11.0.0&lt;/div&gt;&lt;div&gt;npm notice&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;% cd my-electron-app&lt;/div&gt;&lt;div&gt;my-electron-app % npm install express multicast-dns axios&lt;/div&gt;&lt;div&gt;my-electron-app % cat package.json&amp;nbsp;&lt;/div&gt;&lt;div&gt;{&lt;/div&gt;&lt;div&gt;&amp;nbsp; "name": "my-electron-app",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "private": true,&lt;/div&gt;&lt;div&gt;&amp;nbsp; "version": "0.0.0",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "type": "module",&lt;/div&gt;&lt;div&gt;&amp;nbsp; "scripts": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dev": "vite",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "build": "vue-tsc &amp;amp;&amp;amp; vite build &amp;amp;&amp;amp; electron-builder",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "preview": "vite preview"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; "dependencies": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "axios": "^1.7.9",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "express": "^4.21.2",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "multicast-dns": "^7.2.5",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "vue": "^3.4.21"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; "devDependencies": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "@vitejs/plugin-vue": "^5.0.4",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "electron": "^30.0.1",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "electron-builder": "^24.13.3",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "typescript": "^5.2.2",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "vite": "^5.1.6",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "vite-plugin-electron": "^0.28.6",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "vite-plugin-electron-renderer": "^0.14.5",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "vue-tsc": "^2.0.26"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; "main": "dist-electron/main.js"&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;my-electron-app % npm run dev&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如此就有了一個畫面，而目錄結構：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% tree -I 'node_modules|dist|build'&lt;/div&gt;&lt;div&gt;.&lt;/div&gt;&lt;div&gt;├── README.md&lt;/div&gt;&lt;div&gt;├── dist-electron&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;├── main.js&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;└── preload.mjs&lt;/div&gt;&lt;div&gt;├── electron&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;├── electron-env.d.ts&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;├── main.ts&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;└── preload.ts&lt;/div&gt;&lt;div&gt;├── electron-builder.json5&lt;/div&gt;&lt;div&gt;├── index.html&lt;/div&gt;&lt;div&gt;├── package-lock.json&lt;/div&gt;&lt;div&gt;├── package.json&lt;/div&gt;&lt;div&gt;├── public&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;├── electron-vite.animate.svg&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;├── electron-vite.svg&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;└── vite.svg&lt;/div&gt;&lt;div&gt;├── src&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;├── App.vue&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;├── assets&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;│&amp;nbsp; &amp;nbsp;└── vue.svg&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;├── components&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;│&amp;nbsp; &amp;nbsp;└── HelloWorld.vue&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;├── main.ts&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;├── style.css&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp;└── vite-env.d.ts&lt;/div&gt;&lt;div&gt;├── tsconfig.json&lt;/div&gt;&lt;div&gt;├── tsconfig.node.json&lt;/div&gt;&lt;div&gt;└── vite.config.ts&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;7 directories, 22 files&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著，要來擴充一下功能，讓這支程式可以使用 mDNS 協定偵測環境，這功能需實作在 Electron 架構上，在 main.ts 添加裝置搜尋：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ cat electron/main.ts&amp;nbsp;&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;import mdns from 'multicast-dns'&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;// 使用一個全域變數紀錄偵測到的 mDNS 裝置，並記錄他初次出現的時間，也紀錄最後一次看到的時間&lt;/div&gt;&lt;div&gt;const mdnsDevices: { [key: string]: { name: string, type: string, firstSeen: number, lastSeen: number, data: any[], answers: any[] } } = {}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;function startMdnsQuery() {&lt;/div&gt;&lt;div&gt;&amp;nbsp; const mdnsInstance = mdns()&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; console.log('mDNS Query Start...')&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; // 修改 name &amp;amp; type&lt;/div&gt;&lt;div&gt;&amp;nbsp; // dns-sd -B _services._dns-sd._udp local&lt;/div&gt;&lt;div&gt;&amp;nbsp; mdnsInstance.query({&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; questions: [{&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; //name: '_http._tcp.local',&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; name: '_services._dns-sd._udp.local',&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; type: 'PTR'&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }]&lt;/div&gt;&lt;div&gt;&amp;nbsp; })&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; mdnsInstance.on('response', (response: { answers: string | any[] }) =&amp;gt; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; console.log('mDNS Response:', response)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if (win &amp;amp;&amp;amp; response.answers.length &amp;gt; 0) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; for (const answer of response.answers) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 建立一個 unique key 來代表一個裝置&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; const key = `${answer.name}-${answer.type}`&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (!mdnsDevices[key]) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; mdnsDevices[key] = {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; name: answer.name,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; type: answer.type,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; data: [answer.data],&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; firstSeen: Date.now(),&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; lastSeen: Date.now(),&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; answers: [answer]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.log('ADD Device:')&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.log(mdnsDevices[key])&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; } else {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; mdnsDevices[key].lastSeen = Date.now()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; mdnsDevices[key].answers.push(answer)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (!mdnsDevices[key].data.includes(answer.data)) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; mdnsDevices[key].data.push(answer.data)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; win.webContents.send('mdns-found', mdnsDevices)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; } catch (error) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.error(error)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; })&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;app.whenReady().then(() =&amp;gt; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; createWindow()&lt;/div&gt;&lt;div&gt;&amp;nbsp; startMdnsQuery()&lt;/div&gt;&lt;div&gt;})&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在 preload.ts 增加資料回傳到前端網頁上：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ cat electron/preload.ts&amp;nbsp;&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;contextBridge.exposeInMainWorld('mdnsAPI', {&lt;/div&gt;&lt;div&gt;&amp;nbsp; onFound: (callback: (devices: any[]) =&amp;gt; void) =&amp;gt; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; ipcRenderer.on('mdns-found', (_event, devices) =&amp;gt; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; callback(devices)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; })&lt;/div&gt;&lt;div&gt;&amp;nbsp; }&lt;/div&gt;&lt;div&gt;})&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;App.vue:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;&amp;lt;script setup lang="ts"&amp;gt;&lt;/div&gt;&lt;div&gt;import HelloWorld from './components/HelloWorld.vue'&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;import { ref, onMounted } from 'vue'&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;const devices = ref&amp;lt;any[]&amp;gt;([])&lt;/div&gt;&lt;div&gt;onMounted(() =&amp;gt; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; // 若在 preload 中已暴露 mdnsAPI&lt;/div&gt;&lt;div&gt;&amp;nbsp; if (window.mdnsAPI &amp;amp;&amp;amp; typeof window.mdnsAPI.onFound === 'function') {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; window.mdnsAPI.onFound((foundDevices) =&amp;gt; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; // 將接收到的裝置資料放入響應式變數&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; devices.value = foundDevices&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; console.log('Found devices:')&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; console.log(foundDevices)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; })&lt;/div&gt;&lt;div&gt;&amp;nbsp; }&lt;/div&gt;&lt;div&gt;})&lt;/div&gt;&lt;div&gt;&amp;lt;/script&amp;gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;lt;template&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;lt;div&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;lt;a href="https://electron-vite.github.io" target="_blank"&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;img src="/electron-vite.svg" class="logo" alt="Vite logo" /&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/a&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;lt;a href="https://vuejs.org/" target="_blank"&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;img src="./assets/vue.svg" class="logo vue" alt="Vue logo" /&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/a&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;lt;!--&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;lt;HelloWorld msg="Vite + Vue" /&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;lt;hr /&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; --&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;lt;div&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;lt;h3&amp;gt;已搜尋到的裝置：&amp;lt;/h3&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;lt;ul id="deviceList"&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;li v-for="(device, index) in devices" :key="index"&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [{{ new Date(device.firstSeen).toLocaleString('zh-TW', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }) }}] {{ device.name }} ({{ device.type }})&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;ul&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;li v-for="(data, i) in device.data" :key="I"&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {{ data }}&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/li&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/ul&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/li&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/ul&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;lt;/template&amp;gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;lt;style scoped&amp;gt;&lt;/div&gt;&lt;div&gt;.logo {&lt;/div&gt;&lt;div&gt;&amp;nbsp; height: 6em;&lt;/div&gt;&lt;div&gt;&amp;nbsp; padding: 1.5em;&lt;/div&gt;&lt;div&gt;&amp;nbsp; will-change: filter;&lt;/div&gt;&lt;div&gt;&amp;nbsp; transition: filter 300ms;&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;.logo:hover {&lt;/div&gt;&lt;div&gt;&amp;nbsp; filter: drop-shadow(0 0 2em #646cffaa);&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;.logo.vue:hover {&lt;/div&gt;&lt;div&gt;&amp;nbsp; filter: drop-shadow(0 0 2em #42b883aa);&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#deviceList {&lt;/div&gt;&lt;div&gt;&amp;nbsp; text-align: left;&lt;/div&gt;&lt;div&gt;&amp;nbsp; list-style-type: none;&lt;/div&gt;&lt;div&gt;&amp;nbsp; padding: 0;&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;&amp;lt;/style&amp;gt;&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;p style="text-align: left;"&gt;收工 ，有空再來做些什麼應用吧：&lt;a href="https://github.com/changyy/study-my-electron-app" rel="nofollow" target="_blank"&gt;github.com/changyy/study-my-electron-app&lt;/a&gt;&lt;/p&gt;</description><link>http://blog.changyy.org/2024/12/nodejs-electron-vite-vue-pc-app-mdns.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqrmKH3EtBH1ato1hlevc5Y2qbHSaX60ocFR3TEZTdFo0ncp03ZeeifezLDiLrqTsx_k_6QUmBtK7HmpsW3yOFtMJ-kumvTw9z1iCNKJ1ge0_sHe1WIgfFlNk7yRyWXGTYAEuU23GEbHKHkChP4CPvd6JjN6TrfBX58bcKp-MpF52d3KaNUneCQE1SH3yx/s72-w640-h480-c/Screenshot%202024-12-17%20at%2020.00.00.png" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-2883651405078798491</guid><pubDate>Thu, 05 Dec 2024 14:30:00 +0000</pubDate><atom:updated>2024-12-06T08:50:04.786+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">city</category><category domain="http://www.blogger.com/atom/ns#">cityname</category><category domain="http://www.blogger.com/atom/ns#">country</category><category domain="http://www.blogger.com/atom/ns#">countryname</category><category domain="http://www.blogger.com/atom/ns#">geodb</category><category domain="http://www.blogger.com/atom/ns#">geoip</category><category domain="http://www.blogger.com/atom/ns#">geolocation</category><category domain="http://www.blogger.com/atom/ns#">list</category><category domain="http://www.blogger.com/atom/ns#">name</category><category domain="http://www.blogger.com/atom/ns#">php</category><title>PHP 開發筆記 - 產生 CountryCode, CountryName, CityName, GeoLocation 列表</title><description>&lt;div&gt;原本一直盧 AI 產出，發現產出的品質很難控，最後就轉個彎去把 GeoIP DB 內的資料輸出即可，而策略也很簡單，單純用 IP 暴力輪詢。理論上可以優雅一點去了解 DB Record format，總之，暴力解也很快，就順手先記錄一下，此例僅列出部分資訊（非輪詢所有 IPv4）：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;&amp;lt;?php&lt;/div&gt;&lt;div&gt;if (!extension_loaded('geoip')) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; die("GeoIP extension is not installed\n");&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;class GeoIPParser {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; private $dbPath;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; public function __construct($dbPath = '/tmp/db.dat') {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $this-&amp;gt;dbPath = $dbPath;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; geoip_setup_custom_directory('/tmp');&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; public function parse() {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (!file_exists($this-&amp;gt;dbPath)) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new Exception("GeoIP City database not found at {$this-&amp;gt;dbPath}");&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $locations = [];&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $processed = [];&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 遍歷 IP 範圍，每個 /16 subnet 取樣幾個 IP&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for ($first = 1; $first &amp;lt;= 255; $first++) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fprintf(STDERR, "\rProcessing IP block: %d/255", $first);&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for ($second = 0; $second &amp;lt;= 255; $second += 5) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $ip = "$first.$second.1.1";&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $record = geoip_record_by_name($ip);&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if ($record &amp;amp;&amp;amp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; !empty($record['country_code']) &amp;amp;&amp;amp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; !empty($record['city'])) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $key = $record['country_code'] . '|' . $record['city'];&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (!isset($processed[$key])) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $location = [&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'country_code' =&amp;gt; $record['country_code'],&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'country_name' =&amp;gt; $record['country_name'],&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'city_name' =&amp;gt; $record['city'],&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'geo_location' =&amp;gt; [&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'latitude' =&amp;gt; round($record['latitude'], 4),&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'longitude' =&amp;gt; round($record['longitude'], 4)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ];&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $locations[] = $location;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $processed[$key] = true;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fprintf(STDERR, "\nProcessing completed. Total locations found: " . count($locations) . "\n");&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 按國家代碼和城市名稱排序&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; usort($locations, function($a, $b) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $countryComp = strcmp($a['country_code'], $b['country_code']);&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return $countryComp === 0 ?&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; strcmp($a['city_name'], $b['city_name']) :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $countryComp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return $locations;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; } catch (Exception $e) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fprintf(STDERR, "Error during processing: " . $e-&amp;gt;getMessage() . "\n");&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw $e;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;try {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; $parser = new GeoIPParser();&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; $locations = $parser-&amp;gt;parse();&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; // 輸出為格式化的 JSON&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; echo json_encode($locations,&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; JSON_PRETTY_PRINT |&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; JSON_UNESCAPED_UNICODE |&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; JSON_UNESCAPED_SLASHES&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; );&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;} catch (Exception $e) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; fwrite(STDERR, "Error: " . $e-&amp;gt;getMessage() . "\n");&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; exit(1);&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;產出：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% php83 -ini | grep geoip&lt;/div&gt;&lt;div&gt;/opt/local/var/db/php83/geoip.ini,&lt;/div&gt;&lt;div&gt;geoip&lt;/div&gt;&lt;div&gt;geoip support =&amp;gt; enabled&lt;/div&gt;&lt;div&gt;geoip extension version =&amp;gt; 1.1.1&lt;/div&gt;&lt;div&gt;geoip library version =&amp;gt; 1005000&lt;/div&gt;&lt;div&gt;geoip.custom_directory =&amp;gt; no value =&amp;gt; no value&lt;/div&gt;&lt;div&gt;% php83 geo-lookup.php&amp;nbsp;&lt;/div&gt;&lt;div&gt;Processing IP block: 255/255&lt;/div&gt;&lt;div&gt;Processing completed. Total locations found: 2657&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;p&gt;片段資料：&amp;nbsp;&lt;/p&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;% php83 list-geo-city.php | jq '[.[]|select(.country_code=="TW")]'&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;Processing IP block: 255/255&lt;/div&gt;&lt;div&gt;Processing completed. Total locations found: 8020&lt;/div&gt;&lt;div&gt;[&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Anping District",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 22.9965,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.1617&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Bade District",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.9259,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.2763&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Banqiao",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 25.0104,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.4683&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Beitou",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 25.1403,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.4948&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Chang-hua",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.0759,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.5657&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Chiayi City",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.4815,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.4498&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Chiyayi County",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.4461,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.5728&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Daan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 25.0316,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.5345&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Dacun",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.9978,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.547&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Dawan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.2073,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.1906&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Daya",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.2226,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.6493&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Douliu",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.7125,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.545&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "East District",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 22.9721,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.2224&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Guishan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 25.0273,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.359&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Hsinchu",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.8065,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.9706&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Hsinchu County",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.673,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.1614&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Hualien City",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.9807,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.6115&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Jian",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.9516,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.5639&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Jinshan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 25.0613,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.5705&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Kaohsiung City",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 22.6148,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.3139&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Keelung",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 25.1322,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.742&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Linkou District",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 25.0738,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.3935&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Miaoli",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.5641,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.8275&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Nantou City",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.9082,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.6558&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Neihu District",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 25.0811,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.5838&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "New Taipei City",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.9466,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.586&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Penghu County",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.5748,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 119.6098&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Pingtung City",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 22.6745,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.491&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Puli",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.9678,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.9644&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Sanchong District",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 25.0691,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.4878&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Sanxia District",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.9336,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.372&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Shiding District",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.9956,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.6546&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Shoufeng",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.8341,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.521&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Taibao",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.4603,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.3284&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Taichung",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.144,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.6844&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Taichung City",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.1547,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.6716&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Tainan City",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 22.9917,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.2147&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Taipei",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 25.0504,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.5324&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Taitung",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 22.7563,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.1418&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Taoyuan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.9977,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.2965&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Taoyuan District",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.9889,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.3175&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Xizhi District",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 25.0696,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.6577&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Yilan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.7574,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.7421&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Yongkang District",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.0204,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.2591&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Yunlin",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 23.7113,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.3897&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Zhongli District",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.9614,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.2437&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Zhubei",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 24.8351,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 121.0056&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_code": "TW",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "country_name": "Taiwan",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "city_name": "Zuoying",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "geo_location": {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "latitude": 22.6868,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; "longitude": 120.2971&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; }&lt;/div&gt;&lt;div&gt;]&lt;/div&gt;&lt;/blockquote&gt;</description><link>http://blog.changyy.org/2024/12/php-geoip-db-country-city-name-geo-location-list.html</link><author>noreply@blogger.com (Unknown)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-1795519175060067191</guid><pubDate>Wed, 04 Dec 2024 14:01:00 +0000</pubDate><atom:updated>2024-12-04T22:01:00.126+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CDT</category><category domain="http://www.blogger.com/atom/ns#">DST</category><category domain="http://www.blogger.com/atom/ns#">EDT</category><category domain="http://www.blogger.com/atom/ns#">PDT</category><category domain="http://www.blogger.com/atom/ns#">TZ</category><category domain="http://www.blogger.com/atom/ns#">TZDATA</category><category domain="http://www.blogger.com/atom/ns#">TZDB</category><title>PHP 開發筆記 - 關於 DST 日光節約時間的判斷邏輯與實現方式</title><description>&lt;div&gt;這是一個很古老的議題，但因為產品要進行本地化資訊顯示開始提供一些架構，然而，這本身顯示時間上都已經很成熟也有內建 library 可用，但基於一些嵌入式產品的條件，如連網狀態等，需要復刻一版服務出來，像是把日光節約時間的邏輯給列出來等。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;輕鬆問問 Claude.AI 就立馬有了一版，且過程中還發現 AI 採用每日檢查是否是日光節約時間 XD 但明明可以查 tzdata 有效率做完這件事才對。就順手引導 AI ，自己也下去改一下判斷邏輯。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;此外，也透過 AI 補足了一些知識，像是 tzdata 會定期更新，且一年也可能更新數次：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;&lt;a href="https://data.iana.org/time-zones/releases/" rel="nofollow" target="_blank"&gt;data.iana.org/time-zones/releases/&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;隨便下載一份解開來看：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;zone.tab: 全部時區清單，內有 geo location 資訊&lt;/li&gt;&lt;li&gt;northamerica: 北美洲的 DST 規則&lt;/li&gt;&lt;li&gt;europe: 歐洲的 DST 規則&lt;/li&gt;&lt;li&gt;asia: 亞洲的 DST 規則&lt;/li&gt;&lt;li&gt;australasia: 澳洲和亞洲部分地區的 DST 規則&lt;/li&gt;&lt;li&gt;africa: 非洲的 DST 規則&lt;/li&gt;&lt;li&gt;southamerica: 南美洲的 DST 規則&lt;/li&gt;&lt;li&gt;antarctica: 南極洲的 DST 規則&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;而 abbreviation 縮寫意義：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;EST = Eastern Standard Time （東部標準時間）&lt;/li&gt;&lt;li&gt;EDT = Eastern Daylight Time （東部夏令時間）&lt;/li&gt;&lt;li&gt;PST = Pacific Standard Time （太平洋標準時間）&lt;/li&gt;&lt;li&gt;PDT = Pacific Daylight Time （太平洋夏令時間）&lt;/li&gt;&lt;li&gt;CST = Central Standard Time （中部標準時間）&lt;/li&gt;&lt;li&gt;CDT = Central Daylight Time （中部夏令時間）&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;最後，用 PHP Code 順一個出來，這邊 dst_begin 不是日光節約時間真正的起始日，僅是一個趨近資料，只有當該時區不是日光節約時間時，才會是下一個日光節約時段開始的時間&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;&amp;lt;?php&lt;/div&gt;&lt;div&gt;$output = [];&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;$dst_query_start = strtotime("-7 day midnight");&lt;/div&gt;&lt;div&gt;$days_in_year = date('L') ? 366 + 7 : 365 + 7; // 檢查是否為閏年&lt;/div&gt;&lt;div&gt;$dst_query_end = $dst_query_start + (86400 * $days_in_year);&lt;/div&gt;&lt;div&gt;try {&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;foreach(DateTimeZone::listIdentifiers() as $tz) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;$datetime = new DateTime('now', new DateTimeZone($tz));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;$timezone = new DateTimeZone($tz);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;$location = $timezone-&amp;gt;getLocation();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;$transitions = $timezone-&amp;gt;getTransitions($dst_query_start, $dst_query_end);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;$has_dst = false;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;$dst_begin = array( 'datetime' =&amp;gt; NULL, 'timestamp' =&amp;gt; NULL, 'offset' =&amp;gt; NULL, 'abbreviation' =&amp;gt; NULL);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;$dst_end = array( 'datetime' =&amp;gt; NULL, 'timestamp' =&amp;gt; NULL, 'offset' =&amp;gt; NULL, 'abbreviation' =&amp;gt; NULL);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;for ($i=0, $cnt=count($transitions) ; $i &amp;lt; $cnt ; ++$i ) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;			&lt;/span&gt;if ($has_dst == false) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;				&lt;/span&gt;if ($transitions[$i]['isdst']) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;					&lt;/span&gt;$has_dst = true;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;					&lt;/span&gt;$dst_begin = array(&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;						&lt;/span&gt;'datetime' =&amp;gt; date('Y-m-d H:i:s', $transitions[$i]['ts']),&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;						&lt;/span&gt;'timestamp' =&amp;gt; $transitions[$i]['ts'],&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;						&lt;/span&gt;'offset' =&amp;gt; $transitions[$i]['offset'],&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;						&lt;/span&gt;'abbreviation' =&amp;gt; $transitions[$i]['abbr'],&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;					&lt;/span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;				&lt;/span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;			&lt;/span&gt;} else if (!$transitions[$i]['isdst']) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;				&lt;/span&gt;$dst_end = array(&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;					&lt;/span&gt;'datetime' =&amp;gt; date('Y-m-d H:i:s', $transitions[$i]['ts']),&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;					&lt;/span&gt;'timestamp' =&amp;gt; $transitions[$i]['ts'],&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;					&lt;/span&gt;'offset' =&amp;gt; $transitions[$i]['offset'],&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;					&lt;/span&gt;'abbreviation' =&amp;gt; $transitions[$i]['abbr'],&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;				&lt;/span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;				&lt;/span&gt;break;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;			&lt;/span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;array_push($output, array(&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;			&lt;/span&gt;'timezone' =&amp;gt; $tz,&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;			&lt;/span&gt;'offset' =&amp;gt; $datetime-&amp;gt;format('P'),&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;			&lt;/span&gt;'abbreviation' =&amp;gt; $datetime-&amp;gt;format('T'),&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;			&lt;/span&gt;'is_dst' =&amp;gt; (bool)$datetime-&amp;gt;format('I'),&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;			&lt;/span&gt;'dst_info' =&amp;gt; array(&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;				&lt;/span&gt;'begin' =&amp;gt; $dst_begin,&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;				&lt;/span&gt;'end' =&amp;gt; $dst_end,&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;				&lt;/span&gt;'has_dst' =&amp;gt; $has_dst,&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;			&lt;/span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;			&lt;/span&gt;'country_code' =&amp;gt; isset($location['country_code']) &amp;amp;&amp;amp; $location['country_code'] != '??' ? $location['country_code'] : null,&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;			&lt;/span&gt;'latitude' =&amp;gt; isset($location['latitude']) ? $location['latitude'] : null,&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;			&lt;/span&gt;'longitude' =&amp;gt; isset($location['longitude']) ? $location['longitude']: null,&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;		&lt;/span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;} catch (Exception $e) {&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;$output = $e-&amp;gt;getMessage();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;echo json_encode($output, JSON_PRETTY_PRINT)."\n";&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在此刻 2024-12-04 運行 PHP Code 結果，透過 jq 來過濾清單，列出哪些有日光節約時段時區：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;% php83 test.php | jq '[.[] | select(.dst_info.has_dst == true) | {timezone: .timezone, dst_begin: .dst_info.begin.datetime, dst_end: .dst_info.end.datetime}]'&lt;/div&gt;&lt;div&gt;[&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Africa/Cairo",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-04-24 22:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-30 21:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Africa/Casablanca",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-02-23 02:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-06 02:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Africa/Ceuta",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Africa/El_Aaiun",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-02-23 02:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-06 02:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Adak",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 12:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 11:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Anchorage",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 11:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 10:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Asuncion",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-03-23 03:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Boise",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 09:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 08:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Cambridge_Bay",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 09:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 08:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Chicago",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 08:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 07:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Ciudad_Juarez",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 09:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 08:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Denver",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 09:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 08:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Detroit",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Edmonton",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 09:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 08:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Glace_Bay",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 06:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 05:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Goose_Bay",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 06:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 05:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Grand_Turk",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Halifax",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 06:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 05:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Havana",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 05:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 05:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Indiana/Indianapolis",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Indiana/Knox",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 08:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 07:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Indiana/Marengo",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Indiana/Petersburg",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Indiana/Tell_City",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 08:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 07:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Indiana/Vevay",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Indiana/Vincennes",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Indiana/Winamac",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Inuvik",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 09:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 08:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Iqaluit",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Juneau",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 11:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 10:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Kentucky/Louisville",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Kentucky/Monticello",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Los_Angeles",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 10:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 09:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Matamoros",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 08:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 07:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Menominee",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 08:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 07:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Metlakatla",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 11:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 10:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Miquelon",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 05:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 04:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Moncton",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 06:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 05:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Nassau",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/New_York",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Nome",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 11:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 10:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/North_Dakota/Beulah",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 08:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 07:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/North_Dakota/Center",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 08:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 07:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/North_Dakota/New_Salem",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 08:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 07:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Nuuk",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Ojinaga",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 08:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 07:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Port-au-Prince",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Rankin_Inlet",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 08:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 07:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Resolute",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 08:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 07:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Santiago",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-06 03:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Scoresbysund",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Sitka",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 11:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 10:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/St_Johns",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 05:30:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 04:30:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Thule",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 06:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 05:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Tijuana",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 10:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 09:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Toronto",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 07:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 06:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Vancouver",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 10:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 09:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Winnipeg",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 08:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 07:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "America/Yakutat",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 11:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 10:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Antarctica/Macquarie",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-05 16:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Antarctica/McMurdo",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-05 14:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Antarctica/Troll",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Arctic/Longyearbyen",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Asia/Beirut",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-29 22:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-25 21:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Asia/Famagusta",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Asia/Gaza",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-04-12 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-24 23:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Asia/Hebron",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-04-12 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-24 23:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Asia/Jerusalem",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-28 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-25 23:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Asia/Nicosia",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Atlantic/Azores",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Atlantic/Bermuda",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-09 06:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-11-02 05:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Atlantic/Canary",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Atlantic/Faroe",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Atlantic/Madeira",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Australia/Adelaide",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-05 16:30:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Australia/Broken_Hill",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-05 16:30:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Australia/Hobart",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-05 16:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Australia/Lord_Howe",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-05 15:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Australia/Melbourne",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-05 16:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Australia/Sydney",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-05 16:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Amsterdam",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Andorra",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Athens",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Belgrade",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Berlin",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Bratislava",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Brussels",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Bucharest",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Budapest",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Busingen",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Chisinau",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 00:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Copenhagen",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Dublin",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-03-30 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Gibraltar",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Guernsey",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Helsinki",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Isle_of_Man",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Jersey",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Kyiv",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Lisbon",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Ljubljana",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/London",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Luxembourg",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Madrid",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Malta",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Mariehamn",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Monaco",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Oslo",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Paris",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Podgorica",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Prague",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Riga",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Rome",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/San_Marino",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Sarajevo",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Skopje",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Sofia",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Stockholm",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Tallinn",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Tirane",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Vaduz",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Vatican",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Vienna",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Vilnius",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Warsaw",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Zagreb",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Europe/Zurich",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2025-03-30 01:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-10-26 01:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Pacific/Auckland",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-05 14:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Pacific/Chatham",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-05 14:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Pacific/Easter",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-06 03:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;div&gt;&amp;nbsp; {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "timezone": "Pacific/Norfolk",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_begin": "2024-11-27 00:00:00",&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; "dst_end": "2025-04-05 15:00:00"&lt;/div&gt;&lt;div&gt;&amp;nbsp; }&lt;/div&gt;&lt;div&gt;]&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal; white-space: pre;"&gt;	&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;span style="white-space: normal; white-space: pre;"&gt;	&lt;/span&gt;&lt;/p&gt;</description><link>http://blog.changyy.org/2024/12/php-dst-rules-tzdata-tzdb-code.html</link><author>noreply@blogger.com (Unknown)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-1070416928226738625</guid><pubDate>Tue, 19 Nov 2024 12:58:00 +0000</pubDate><atom:updated>2024-11-20T06:14:41.239+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">check</category><category domain="http://www.blogger.com/atom/ns#">ldap</category><category domain="http://www.blogger.com/atom/ns#">openldap</category><category domain="http://www.blogger.com/atom/ns#">openssl</category><category domain="http://www.blogger.com/atom/ns#">ssl</category><category domain="http://www.blogger.com/atom/ns#">starttls</category><category domain="http://www.blogger.com/atom/ns#">tls</category><category domain="http://www.blogger.com/atom/ns#">verify</category><title>Linux 開發筆記 - OpenLDAP StartTLS 憑證驗證與故障排除 @ Ubuntu 20.04</title><description>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUPRkqucyA9hMIfs_iCOVkI0cP5RJj7ppJqF5COVtFKbGugiLKzl5IduKoQxpsEoGakP0BHGkqGYv4n4vQLvaXyCdNZVjIiyKZ1HRDbORVbQ4z8IXPhRKEi4Gy96UUeiABxdpxLgDzlHNQ3r7H_Ukmke9rdqN5RCEZRd1YHQFmmO3dGctZHwovGmR72uu0/s1396/openldap.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="250" data-original-width="1396" height="114" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUPRkqucyA9hMIfs_iCOVkI0cP5RJj7ppJqF5COVtFKbGugiLKzl5IduKoQxpsEoGakP0BHGkqGYv4n4vQLvaXyCdNZVjIiyKZ1HRDbORVbQ4z8IXPhRKEi4Gy96UUeiABxdpxLgDzlHNQ3r7H_Ukmke9rdqN5RCEZRd1YHQFmmO3dGctZHwovGmR72uu0/w640-h114/openldap.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;負責 DevOps 同事休假，當初 LDAP 架設沒涉入，用 AI 輔助來練一下功，盡量讓同事好好休假不用 oncall 解題，就把這過程筆記一下。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;現在有 AI 輔助服務，做事的心態變了不少，包括：&lt;/div&gt;&lt;div&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;面對不是自己從頭到尾參與的計劃時，該怎樣建立自己了解系統思維或是達成想做的任務&lt;/li&gt;&lt;li&gt;AI 的答案也不一定是對的，該如何驗證（如同管理或跨團隊合作上，請夥伴做事，但不一定做對，該怎樣驗正）&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;事件起因是一些小型內部服務，因 LDAP 憑證失效而無法完成登入（已登入不受影響），我的追法：&lt;/div&gt;&lt;div&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;請 AI 給我指令或程式，檢驗快速檢驗 ldap 連線過程，著重在憑證檢查，例如檢查 https 憑證最常就是靠 openssl 指令或是 browser 瀏覽檢視憑證&lt;/li&gt;&lt;li&gt;有時 AI 會回的很搞剛，開始回寫 code 的 python 解法，這時因為自己有經驗 openssl 指令就能搞定，所以不斷提醒 AI 直接給指令解&lt;/li&gt;&lt;li&gt;對於 OpenLDAP 不熟的我，開始追問 AI 憑證相關的方向，才順勢了解 StartTLS ，才知道有 ldapsearch, slaptest 指令可用&lt;/li&gt;&lt;li&gt;對於管理線上服務踩過雷，知道不能隨便 service restart，多問一下 AI 該怎樣先檢驗設定檔，避免機器從 running -&amp;gt; stop -&amp;gt; 設定有誤 -&amp;gt; 服務停止營運&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;使用 openssl 指令檢查 SSL 憑證，假設網域是 ldap-dmz.changyy.org&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ openssl s_client -connect ldap-dmz.changyy.org:443&amp;nbsp;&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;---&lt;/div&gt;&lt;div&gt;SSL handshake has read xxxx bytes and written xxx bytes&lt;/div&gt;&lt;div&gt;Verification: OK&lt;/div&gt;&lt;div&gt;---&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;使用 openssl 指令檢查 LDAP + StartTLS 憑證，假設網域是 ldap-dmz.changyy.org&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ openssl s_client -connect ldap-dmz.changyy.org:389 -starttls ldap&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;---&lt;/div&gt;&lt;div&gt;SSL handshake has read xxxx bytes and written xxx bytes&lt;/div&gt;&lt;div&gt;Verification error: certificate has expired&lt;/div&gt;&lt;div&gt;---&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;得知 LDAP + StartTLS 使用流程中的憑證過期了，且此例剛好 https 憑證已正常工作，僅 LDAP + StartTLS 的部分失敗，所以很輕鬆的可以把 https 憑證複製一份到 ldap 使用即可。先追蹤設定檔位置：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ sudo cat /etc/ldap/slapd.d/cn\=config.ldif | grep -i TLS&lt;/div&gt;&lt;div&gt;olcTLSCACertificateFile: /etc/ssl/ldap/wildcard.*.ca-bundle&lt;/div&gt;&lt;div&gt;olcTLSCertificateFile: /etc/ssl/ldap/wildcard.*.crt&lt;/div&gt;&lt;div&gt;olcTLSCertificateKeyFile: /etc/ssl/ldap/wildcard.*.key&lt;/div&gt;&lt;div&gt;$ sudo tree -L 1 /etc/ssl/&lt;/div&gt;&lt;div&gt;/etc/ssl/&lt;/div&gt;&lt;div&gt;├── certs&lt;/div&gt;&lt;div&gt;├── ldap&lt;/div&gt;&lt;div&gt;├── nginx&lt;/div&gt;&lt;div&gt;├── openssl.cnf&lt;/div&gt;&lt;div&gt;└── private&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;速速解：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ sudo cp -r /etc/ssl/ldap /etc/ssl/ldap-bak&lt;/div&gt;&lt;div&gt;$ sudo cp /etc/ssl/nginx/wildcard.* /etc/ssl/ldap/&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;檢驗設定檔和重啟服務：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ sudo slaptest&lt;/div&gt;&lt;div&gt;config file testing succeeded&lt;/div&gt;&lt;div&gt;$ sudo service slapd restart&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;再次驗證：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ openssl s_client -connect ldap-dmz.changyy.org:389 -starttls ldap&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;---&lt;/div&gt;&lt;div&gt;SSL handshake has read xxxx bytes and written xxx bytes&lt;/div&gt;&lt;div&gt;Verification: OK&lt;/div&gt;&lt;div&gt;---&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;此外，追蹤 LDAP 和使用他的服務時，意外發現不同的服務整合過程，也順手筆記一下，例如 Apache2 設定檔：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;僅 LDAP 非加密：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;&amp;lt;Location /&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Order allow,deny&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Allow from all&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; AuthType basic&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; AuthName "service.changyy.org"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; AuthBasicProvider ldap&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; AuthLDAPUrl "ldap://ldap-dmz.changyy.org/dc=changyy,dc=org?uid"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Require valid-user&lt;/div&gt;&lt;div&gt;&amp;lt;/Location&amp;gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;LDAP + StartTLS：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;&amp;lt;Location /&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Order allow,deny&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Allow from all&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; AuthType basic&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; AuthName "service.changyy.org"&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; AuthBasicProvider ldap&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; AuthLDAPUrl "ldap://ldap-dmz.changyy.org/dc=changyy,dc=org?uid" TLS&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Require valid-user&lt;/div&gt;&lt;div&gt;&amp;lt;/Location&amp;gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;對應的其他服務也會有類似架構，舉例來說 Mantis 也有 &lt;a href="https://mantisbt.org/docs/master/en-US/Admin_Guide/html/admin.config.auth.ldap.html" rel="nofollow" target="_blank"&gt;mantisbt.org/docs/master/en-US/Admin_Guide/html/admin.config.auth.ldap.html&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;預設是啟用 LDAP + StartTLS&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;//&lt;/div&gt;&lt;div&gt;// Determines whether the connection will attempt an opportunistic upgrade to a TLS connection (STARTTLS).&lt;/div&gt;&lt;div&gt;//&lt;/div&gt;&lt;div&gt;// Defaults to ON.&lt;/div&gt;&lt;div&gt;//&lt;/div&gt;&lt;div&gt;// $g_ldap_use_starttls = ON&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;需要測試 LDAP 健康情況時，可以把它關閉&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;// $g_ldap_use_starttls = OFF&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;補充檢查 ldap service 健康情況時，最常是直接使用 ldapsearch 指令：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ sudo apt install ldap-utils&lt;/div&gt;&lt;div&gt;$ ldapsearch -x -H 'ldap://ldap-dmz.changyy.org' -b 'dc=changyy,dc=org'&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;# search result&lt;/div&gt;&lt;div&gt;search: #&lt;/div&gt;&lt;div&gt;result: 0 Success&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;# numResponses: ###&lt;/div&gt;&lt;div&gt;# numEntries: ###&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;測試 LDAP + StartTLS：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ ldapsearch -x -H 'ldap://ldap-dmz.changyy.org' -b 'dc=changyy,dc=org' -Z&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;ldap_start_tls: Connect error (-11)&lt;/div&gt;&lt;div&gt;&lt;span style="white-space: normal;"&gt;&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;additional info: (unknown error code)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;ldap_result: Can't contact LDAP server (-1)&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;測試 LDAP + StartTLS 需要更多 debug 資訊：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ ldapsearch -x -H 'ldap://ldap-dmz.changyy.org' -b 'dc=changyy,dc=org' -Z -d 1&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;TLS: peer cert untrusted or revoked (0x402)&lt;/div&gt;&lt;div&gt;TLS: can't connect: (unknown error code).&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;ldap_start_tls: Connect error (-11)&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;用 PHP Code 嘗試建立連線，先確認 php.ini 套件：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;php -i | grep ldap&lt;/div&gt;&lt;div&gt;/etc/php/7.4/cli/conf.d/20-ldap.ini,&lt;/div&gt;&lt;div&gt;Protocols =&amp;gt; dict, file, ftp, ftps, gopher, http, https, imap, imaps, ldap, ldaps, pop3, pop3s, rtmp, rtsp, scp, sftp, smb, smbs, smtp, smtps, telnet, tftp&lt;/div&gt;&lt;div&gt;ldap&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;運行：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ cat /tmp/t.php&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;lt;?php&lt;/div&gt;&lt;div&gt;$ldapconn = ldap_connect("ldap://ldap-dmz.changyy.org");&lt;/div&gt;&lt;div&gt;if ($ldapconn) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; // 關閉 TLS&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; putenv('LDAPTLS_REQCERT=never');&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; $bind = ldap_bind($ldapconn);&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if ($bind) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo "LDAP bind successful...\n";&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; } else {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo "LDAP bind failed...\n";&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;$ php /tmp/t.php&amp;nbsp;&lt;/div&gt;&lt;div&gt;LDAP bind successful...&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;收工!&lt;/div&gt;</description><link>http://blog.changyy.org/2024/11/linux-ldap-openldap-tls-check.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUPRkqucyA9hMIfs_iCOVkI0cP5RJj7ppJqF5COVtFKbGugiLKzl5IduKoQxpsEoGakP0BHGkqGYv4n4vQLvaXyCdNZVjIiyKZ1HRDbORVbQ4z8IXPhRKEi4Gy96UUeiABxdpxLgDzlHNQ3r7H_Ukmke9rdqN5RCEZRd1YHQFmmO3dGctZHwovGmR72uu0/s72-w640-h114-c/openldap.png" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-8134123804128479597</guid><pubDate>Sat, 09 Nov 2024 01:03:00 +0000</pubDate><atom:updated>2024-11-26T17:26:47.749+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cve</category><category domain="http://www.blogger.com/atom/ns#">embedded linux</category><category domain="http://www.blogger.com/atom/ns#">EU</category><category domain="http://www.blogger.com/atom/ns#">GB</category><category domain="http://www.blogger.com/atom/ns#">http</category><category domain="http://www.blogger.com/atom/ns#">https</category><category domain="http://www.blogger.com/atom/ns#">iot</category><category domain="http://www.blogger.com/atom/ns#">linux</category><category domain="http://www.blogger.com/atom/ns#">PSTI</category><category domain="http://www.blogger.com/atom/ns#">RED-DA</category><category domain="http://www.blogger.com/atom/ns#">security</category><title>IoT 開發筆記 - 關於歐盟 無線電設備指令 (Radio Equipment Directive, RED) RED-DA 對於聯網設備的資安自我檢查辦法</title><description>&lt;p&gt;這兩年英國、新加坡、歐盟都有滿多資安相關的議題，例如英國 Product Security and Telecommunications Infrastructure (PSTI)&amp;nbsp;產品安全和電信基礎設施法案在 2024.04 生效，現在則幫忙公司處理歐盟 2025年夏天要推進的無線設備指令—委託法案 (Radio Equipment Directive – Delegated Act, 簡稱 RED-DA )&lt;/p&gt;&lt;p&gt;已知的粗略資訊：&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;商品若有聯網功能，需要依照 RED-DA 的要求自我檢驗一番，才能把商品賣進歐盟國家&lt;/li&gt;&lt;li&gt;尚未有中央單位負責檢驗，各大品牌找一些值得信任檢驗單位，請他們檢驗商品出檢驗報告，未來商品進歐盟時，被要求提供報告時，可以拿出來用&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;因此，產品負責人或銷售業務，其實要做的是趕緊找一間信任的資安檢驗公司，提出商品檢驗的需求，價格通常也不便宜，最重要檢驗的時程也不短，通常簽約繳件後也要數個月才會產出報告的，而報告其實是會高度依賴商品的使用情境跟品牌/廠牌，基本上代工廠不能公版做完一份就沒事，是品牌商也得依序產報告（找同一間資安公司，應當有些優惠吧？可以讓資安公司省去摸索商品，且相似的商品可以讓驗證機制加速）&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;總之，就會進行一些聯網設備(IoT)的處理，通常，資安公司也高度依賴產品公司的 IT 部門或負責研發的開發者提供的資訊。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;為了加速檢驗報告的產生，公司可以先自行體檢：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;使用&amp;nbsp;OpenVAS Scanner 等類似工具對聯網設備進行弱點掃描&lt;/li&gt;&lt;li&gt;請開發者整理產品用到了相關 libraries ，透過 CVE 資料庫進行查詢，若發現有漏洞就評估該怎樣更新&lt;/li&gt;&lt;li&gt;若像 WIFI AP 提供 http://192.168.1.1 登入後台使用，那至少必須改提供 https://192.168.1.1 自簽憑證方案&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;由於檢驗報告項目眾多，單純先列一下可以自我檢驗的部分，其他的部分，還是交給資安業者，讓專業的來吧&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;筆記一下，怎樣用 OpenVAS 來掃設備，當然，這是個 Docker 當道的時代，直接跑 Docker 即可，收工（誤），實測在 macOS 15 和 Windows 11 都可以正常運作的：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;C:\Users\User&amp;gt;docker run -p 8443:443 --name openvas mikesplain/openvas&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Unable to find image 'mikesplain/openvas:latest' locally&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;latest: Pulling from mikesplain/openvas&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;34667c7e4631: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;d18d76a881a4: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;119c7358fbfc: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;2aaf13f3eff0: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;67b182362ac2: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;c878d3d5e895: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;ec12cc49fe18: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;c4c454aeebef: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;27d3410150b2: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;e08d578dc278: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;44951337cd32: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;8c7fe885e62a: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;a4f833680e45: Pull complete&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Digest: sha256:23c8412b5f9f370ba71e5cd3db36e6f2e269666cd8a3e3e7872f20f8063b2752&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Status: Downloaded newer image for mikesplain/openvas:latest&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Testing redis status...&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Redis not yet ready...&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Redis ready.&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Checking for empty volume&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Restarting services&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;* Restarting openvas-scanner openvassd&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;...done.&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;* Restarting openvas-manager openvasmd&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;...done.&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;* Restarting openvas-gsa gsad&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;...done.&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Reloading NVTs&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Rebuilding NVT cache... done.&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Checking setup&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;openvas-check-setup 2.3.3&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;Test completeness and readiness of OpenVAS-9&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 19px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;Please report us any non-detected problems and&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;help us to improve this check routine:&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;http://lists.wald.intevation.org/mailman/listinfo/openvas-discuss&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 19px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;Send us the log-file (/tmp/openvas-check-setup.log) to help analyze the problem.&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 19px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;Use the parameter --server to skip checks for client tools&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;like GSD and OpenVAS-CLI.&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 19px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Step 1: Checking OpenVAS Scanner ...&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;OK: OpenVAS Scanner is present in version 5.1.3.&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;OK: OpenVAS Scanner CA Certificate is present as .&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;...&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;p&gt;接著就在本機開啟 https://localhost:8443/ 來運作：&lt;/p&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;p style="text-align: left;"&gt;添加一則 task (左上角的紫色 icon ) -&amp;gt; 指定設備的 IP -&amp;gt; 右下角有 Actions 記得按下 Play icon&amp;nbsp;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIsDoPAPyyK_i0_F6DrNVmslWMQHzGtDTNfcPQjNq0Cupd0GpcpN7sKzfcR0XWSL0yKVioBFFT7qrRpu_hBuMe3t-gNbV7qpnVRlSXxmqa1FX-BGa4mphGl-Ta-cYdOu7q7-OAXlhvD1v6hJDDAlmqOQb1FB80Ek2mlLK5_RHSaNiBIzts_lPUTT0PrXiR/s944/openvas-02.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="944" data-original-width="854" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIsDoPAPyyK_i0_F6DrNVmslWMQHzGtDTNfcPQjNq0Cupd0GpcpN7sKzfcR0XWSL0yKVioBFFT7qrRpu_hBuMe3t-gNbV7qpnVRlSXxmqa1FX-BGa4mphGl-Ta-cYdOu7q7-OAXlhvD1v6hJDDAlmqOQb1FB80Ek2mlLK5_RHSaNiBIzts_lPUTT0PrXiR/w361-h400/openvas-02.png" width="361" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCmGaPJ0Lr9aNP_2aJf3KcST_YL41AgO_caHcx7X7OlC5Zavyvumf2VjBVWj38wtrgaDG05o9Ium4Pta4YKsHq0fA52N5o17MevspAzJdO9ZRte4jLEUmrY9ym_3N5ZInugP94O5nE_HoEZnp7yPfH45WZ9qZFlMs0hgmIWVzwyjiEMUnw348jcpyCoJYe/s1720/openvas-03.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1164" data-original-width="1720" height="271" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCmGaPJ0Lr9aNP_2aJf3KcST_YL41AgO_caHcx7X7OlC5Zavyvumf2VjBVWj38wtrgaDG05o9Ium4Pta4YKsHq0fA52N5o17MevspAzJdO9ZRte4jLEUmrY9ym_3N5ZInugP94O5nE_HoEZnp7yPfH45WZ9qZFlMs0hgmIWVzwyjiEMUnw348jcpyCoJYe/w400-h271/openvas-03.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdiXQIVosOdFeyoxIkYGwih84zQPEG8muMe6Jlxed2JV1eKqenpCLhlNINkSspg8EsQng0U__kuURel8GnhC7_yO6uUwdCk6_4rgAJEFm5ofObBe4oD2xwvjWULUwCAV35OpORoBPLlZP0T4uN21gSGUYf_YzybqH2xxJfuS4rdvmyrIidHEcrPzZ-e-0G/s2228/openvas-04.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1152" data-original-width="2228" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdiXQIVosOdFeyoxIkYGwih84zQPEG8muMe6Jlxed2JV1eKqenpCLhlNINkSspg8EsQng0U__kuURel8GnhC7_yO6uUwdCk6_4rgAJEFm5ofObBe4oD2xwvjWULUwCAV35OpORoBPLlZP0T4uN21gSGUYf_YzybqH2xxJfuS4rdvmyrIidHEcrPzZ-e-0G/w400-h206/openvas-04.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;運行好一陣子後，就會看到一些報告了，像是幫你檢驗 Web server 的版本是否有漏洞、網頁上是否有需要更新的 js libraries，例如：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVVSZEPvHxgdYD4UdroJsuGNme_g5amEbNatRma5xnuvo-HPogypc3CAUxUZvhcgIL0ycwOHuFpsaTTnthIuNs3aHrTQZozcQgrYFiPLWFyMf3TV1tmeLRKGJq3jcz_1jx25hCxoMFKEcFeinYpohVjWRMmhBldelXMnNjwnuEXUIPQmC9S2pzBHx7Kaio/s2232/openvas-05.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1086" data-original-width="2232" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVVSZEPvHxgdYD4UdroJsuGNme_g5amEbNatRma5xnuvo-HPogypc3CAUxUZvhcgIL0ycwOHuFpsaTTnthIuNs3aHrTQZozcQgrYFiPLWFyMf3TV1tmeLRKGJq3jcz_1jx25hCxoMFKEcFeinYpohVjWRMmhBldelXMnNjwnuEXUIPQmC9S2pzBHx7Kaio/w400-h195/openvas-05.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;至於嵌入式系統內部的自我檢查，其實可以善用美國國家單位提供的服務：&amp;nbsp;&lt;a href="https://nvd.nist.gov/vuln/search" rel="nofollow" target="_blank"&gt;nvd.nist.gov/vuln/search&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjItFncg9kae-ZnTYsK3reCrpF9l9frYxjI7FRTXn_FHb5jForNwNLw36hlQ2KghryXCGWpJoMeF9CnyMT83E4V7QRTVREIICD9GwbpnnzsoVqXT7IjBDRGj1bD-ud2M5uPbs_gKcELvJBctOVrCuTClu4eqyne_zZHFA0kSYu3ZezzBg7XzwVmPpOfzoeM/s2764/nvd-01.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1840" data-original-width="2764" height="266" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjItFncg9kae-ZnTYsK3reCrpF9l9frYxjI7FRTXn_FHb5jForNwNLw36hlQ2KghryXCGWpJoMeF9CnyMT83E4V7QRTVREIICD9GwbpnnzsoVqXT7IjBDRGj1bD-ud2M5uPbs_gKcELvJBctOVrCuTClu4eqyne_zZHFA0kSYu3ZezzBg7XzwVmPpOfzoeM/w400-h266/nvd-01.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;在那邊去輸入資訊查詢，像是 curl 啊，等等&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZnizxWKzRtDv-K8ZTe5jhuOmZuaPcWoLHoz8v9JZsqyVFsSBLrHn9HXCY7bMDB9Qx8efc_Q8JuhQPMrfusWyByWH5jR78j2c7yX83ugCVEM7I6vvhLg4T-lpVv7KH_NhTF2uJOeAry3f_N636KaJ_DxVlescZaiX_5tTrktUGTzV3wWBfycLaHX_Q5J_5/s3082/nvd-02.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="2122" data-original-width="3082" height="275" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZnizxWKzRtDv-K8ZTe5jhuOmZuaPcWoLHoz8v9JZsqyVFsSBLrHn9HXCY7bMDB9Qx8efc_Q8JuhQPMrfusWyByWH5jR78j2c7yX83ugCVEM7I6vvhLg4T-lpVv7KH_NhTF2uJOeAry3f_N636KaJ_DxVlescZaiX_5tTrktUGTzV3wWBfycLaHX_Q5J_5/w400-h275/nvd-02.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;當然，這邊純人工檢查極累 Orz 所以，也有 API 服務可以申請，就能夠批次詢問了，細節請參考：&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;&lt;a href="https://nvd.nist.gov/developers/vulnerabilities" rel="nofollow" target="_blank"&gt;nvd.nist.gov/developers/vulnerabilities&lt;/a&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;p style="text-align: left;"&gt;最後，把玩一下小工具：&lt;a href="https://pypi.org/project/cve-vulnerability-scanner/" rel="nofollow" target="_blank"&gt;pypi.org/project/cve-vulnerability-scanner/&lt;/a&gt;&lt;/p&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;% NVD_API_KEY='XXXX-XXXX-XXXX-XXXX' cve-vulnerability-scanner -p zlib -v 1.2.13 -o /tmp/output.md ; cat /tmp/output.md&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Scanning zlib version 1.2.13&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Loading cached data for zlib&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Scan complete. Report generated in /tmp/output.md&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;# Security Vulnerability Report&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Generated on: 2024-11-09 08:53:47&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;## zlib 1.2.13&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;### CVE-2023-45853&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Severity: CRITICAL&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;CVSS Score: 9.8&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Version Range: * to 1.3&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Published: 2023-10-14T02:15:09.323&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Last Modified: 2024-08-01T13:44:58.990&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Description: MiniZip in zlib through 1.3 has an integer overflow and resultant heap-based buffer overflow in zipOpenNewFileInZip4_64 via a long filename, comment, or extra field. NOTE: MiniZip is not a supported part of the zlib product. NOTE: pyminizip through 0.2.6 is also vulnerable because it bundles an affected zlib version, and exposes the applicable MiniZip code through its compress API.&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;### CVE-2023-48106&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Severity: HIGH&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;CVSS Score: 8.8&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Version Range: * to *&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Published: 2023-11-22T18:15:09.630&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Last Modified: 2023-12-02T00:27:03.327&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Description: Buffer Overflow vulnerability in zlib-ng minizip-ng v.4.0.2 allows an attacker to execute arbitrary code via a crafted file to the mz_path_resolve function in the mz_os.c file.&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;### CVE-2023-48107&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Severity: HIGH&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;CVSS Score: 8.8&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Version Range: * to *&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Published: 2023-11-22T23:15:10.663&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Last Modified: 2023-12-27T04:15:07.277&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Description: Buffer Overflow vulnerability in zlib-ng minizip-ng v.4.0.2 allows an attacker to execute arbitrary code via a crafted file to the mz_path_has_slash function in the mz_os.c file.&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;### CVE-2023-6992&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Severity: MEDIUM&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;CVSS Score: 5.5&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Version Range: * to 2023-11-16&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Published: 2024-01-04T12:15:23.690&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Last Modified: 2024-01-10T01:14:35.027&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Description: Cloudflare version of zlib library was found to be vulnerable to memory corruption issues affecting the deflation algorithm implementation (deflate.c). The issues resulted from improper input validation and heap-based buffer overflow.&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;A local attacker could exploit the problem during compression using a crafted malicious file potentially leading to denial of service of the software.&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Patches: The issue has been patched in commit&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;8352d10 https://github.com/cloudflare/zlib/commit/8352d108c05db1bdc5ac3bdf834dad641694c13c . The upstream repository is not affected.&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;### CVE-2003-0107&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Severity: UNKNOWN&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;CVSS Score: 0.0&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Version Range: * to *&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Published: 2003-03-07T05:00:00.000&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Last Modified: 2022-06-22T16:40:46.327&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Description: Buffer overflow in the gzprintf function in zlib 1.1.4, when zlib is compiled without vsnprintf or when long inputs are truncated using vsnprintf, allows attackers to cause a denial of service or possibly execute arbitrary code.&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;### CVE-2004-0797&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Severity: UNKNOWN&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;CVSS Score: 0.0&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Version Range: * to *&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Published: 2004-10-20T04:00:00.000&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Last Modified: 2022-06-22T16:40:46.360&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Description: The error handling in the (1) inflate and (2) inflateBack functions in ZLib compression library 1.2.x allows local users to cause a denial of service (application crash).&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;### CVE-2005-2096&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Severity: UNKNOWN&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;CVSS Score: 0.0&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Version Range: * to *&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Published: 2005-07-06T04:00:00.000&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Last Modified: 2022-06-22T16:40:46.413&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Description: zlib 1.2 and later versions allows remote attackers to cause a denial of service (crash) via a crafted compressed stream with an incomplete code description of a length greater than 1, which leads to a buffer overflow, as demonstrated using a crafted PNG file.&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;### CVE-2005-1849&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Severity: UNKNOWN&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;CVSS Score: 0.0&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Version Range: * to *&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Published: 2005-07-26T04:00:00.000&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Last Modified: 2022-06-22T16:40:46.380&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Description: inftrees.h in zlib 1.2.2 allows remote attackers to cause a denial of service (application crash) via an invalid file that causes a large dynamic tree to be produced.&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;### CVE-2009-1391&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Severity: UNKNOWN&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;CVSS Score: 0.0&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Version Range: * to 2.015&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Published: 2009-06-16T23:30:00.203&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Last Modified: 2018-10-03T22:00:28.997&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Description: Off-by-one error in the inflate function in Zlib.xs in Compress::Raw::Zlib Perl module before 2.017, as used in AMaViS, SpamAssassin, and possibly other products, allows context-dependent attackers to cause a denial of service (hang or crash) via a crafted zlib compressed stream that triggers a heap-based buffer overflow, as exploited in the wild by Trojan.Downloader-71014 in June 2009.&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;### CVE-2009-1391&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Severity: UNKNOWN&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;CVSS Score: 0.0&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Version Range: * to *&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Published: 2009-06-16T23:30:00.203&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Last Modified: 2018-10-03T22:00:28.997&lt;/span&gt;&lt;/p&gt;&lt;p class="p1" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="s1" style="font-variant-ligatures: no-common-ligatures;"&gt;Description: Off-by-one error in the inflate function in Zlib.xs in Compress::Raw::Zlib Perl module before 2.017, as used in AMaViS, SpamAssassin, and possibly other products, allows context-dependent attackers to cause a denial of service (hang or crash) via a crafted zlib compressed stream that triggers a heap-based buffer overflow, as exploited in the wild by Trojan.Downloader-71014 in June 2009.&lt;/span&gt;&lt;/p&gt;&lt;p class="p2" style="background-color: rgba(0, 0, 0, 0.85); color: #f2f2f2; font-family: Monaco; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size-adjust: none; font-size: 18px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px; min-height: 25px;"&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;這工具純把玩，困難之處就是做 version 和 library name 比對，這工具純示意，仍有非常大進步的空間，甚至該把這空間交給專業的資安公司就好，畢竟這是個分工的時代，且需要大量的人力檢視 Orz&lt;/p&gt;</description><link>http://blog.changyy.org/2024/11/iot-product-EU-RED-DA-GB-PSTI.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIsDoPAPyyK_i0_F6DrNVmslWMQHzGtDTNfcPQjNq0Cupd0GpcpN7sKzfcR0XWSL0yKVioBFFT7qrRpu_hBuMe3t-gNbV7qpnVRlSXxmqa1FX-BGa4mphGl-Ta-cYdOu7q7-OAXlhvD1v6hJDDAlmqOQb1FB80Ek2mlLK5_RHSaNiBIzts_lPUTT0PrXiR/s72-w361-h400-c/openvas-02.png" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-1418564191663600283</guid><pubDate>Thu, 07 Nov 2024 13:54:00 +0000</pubDate><atom:updated>2024-12-16T15:19:00.129+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cygwin</category><category domain="http://www.blogger.com/atom/ns#">openssh server</category><category domain="http://www.blogger.com/atom/ns#">Reinstall</category><category domain="http://www.blogger.com/atom/ns#">reset</category><category domain="http://www.blogger.com/atom/ns#">windows</category><title>Windows 開發筆記 - 將 Windows 11 筆電作業系統重置/還原至乾淨的環境與建立本地帳號密碼登入方式</title><description>&lt;div&gt;由於 Windows 筆電常拿來實驗軟體，環境裝得有點亂，把它重置一下，筆記一下流程，哪天應該會用到：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;系統 -&amp;gt; 復原 -&amp;gt; 重設此電腦 -&amp;gt; 本機重新安裝 -&amp;gt; 變更設定 -&amp;gt; 是否清除資料 (是) -&amp;gt; 按下重設就開始進行&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHZFy48tpHiv4NKguPueZlDcIxdnldv3AMn4ZrMQ4r53RBxDHuQE139cHqcDlnMZfXMQgXfFfIIzHG-NU99Bd_FLW8SCj4YPxPbf-ju5eq6EFinku15lJ9JDUOch8tWzkAcmPjVEEfI96nSjRW_EKZYWB95ScpWpSbc4IeyWR2uXziiyRsyF9keD8CzAGc/s1014/02-Reset-Windows11.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="672" data-original-width="1014" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHZFy48tpHiv4NKguPueZlDcIxdnldv3AMn4ZrMQ4r53RBxDHuQE139cHqcDlnMZfXMQgXfFfIIzHG-NU99Bd_FLW8SCj4YPxPbf-ju5eq6EFinku15lJ9JDUOch8tWzkAcmPjVEEfI96nSjRW_EKZYWB95ScpWpSbc4IeyWR2uXziiyRsyF9keD8CzAGc/s320/02-Reset-Windows11.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgFtz921um6yQkMOc0D1Pq1xgKmnah-ePyBqNprhx58aOCjzqh3nRvCyv_7OyNoyRn-6FSRtk4ioZ6G6nLPdtPhKqfAHK_WrHG-9BMgk8Uyq1u3eP69futxM4fIVe6MIZF5kaQdFukPNQaSlvxg0h6iAjykA6ZI2E_xsTSVHUc-_L82NDBgxnebGK9mjrT/s1013/04-Reset-Windows11.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="666" data-original-width="1013" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgFtz921um6yQkMOc0D1Pq1xgKmnah-ePyBqNprhx58aOCjzqh3nRvCyv_7OyNoyRn-6FSRtk4ioZ6G6nLPdtPhKqfAHK_WrHG-9BMgk8Uyq1u3eP69futxM4fIVe6MIZF5kaQdFukPNQaSlvxg0h6iAjykA6ZI2E_xsTSVHUc-_L82NDBgxnebGK9mjrT/s320/04-Reset-Windows11.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh_qFvXCsUhrh3zUNwrJmL8fTYg-QtAzg-mVEoaYSpnClT1L8nVMfTQujjduTNCjGVDiMdFgEXwa0gxqM5vECn4JFTJdr1HSTRhyE_D0wece5VUlVUOrSaMR5al4nCXPCnmQ7izg7-oEtTLKPvLzmFC_75dn6Wrjq1XUALGxcWEByMqFMjSoHw541bw2iF/s1012/05-Reset-Windows11.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="663" data-original-width="1012" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh_qFvXCsUhrh3zUNwrJmL8fTYg-QtAzg-mVEoaYSpnClT1L8nVMfTQujjduTNCjGVDiMdFgEXwa0gxqM5vECn4JFTJdr1HSTRhyE_D0wece5VUlVUOrSaMR5al4nCXPCnmQ7izg7-oEtTLKPvLzmFC_75dn6Wrjq1XUALGxcWEByMqFMjSoHw541bw2iF/s320/05-Reset-Windows11.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhegvW8ASU93nuE3Idt2_n6_NLgfcm0SnLCtJmeW36COz78pWtHt_sXNyd9P_dg-hSLLyePq6oTSLrUBDj7Fj0IiPu5lSzdrBKI-rLR373C5aiGAALPn_deWEEPnX6Z6sIqbhagwGEa5KlrmTEUXik1KzYgW6IxuMt1PRRpIq4CM48btyD61bKu4bJkurHE/s1012/05-Reset-Windows11.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="663" data-original-width="1012" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhegvW8ASU93nuE3Idt2_n6_NLgfcm0SnLCtJmeW36COz78pWtHt_sXNyd9P_dg-hSLLyePq6oTSLrUBDj7Fj0IiPu5lSzdrBKI-rLR373C5aiGAALPn_deWEEPnX6Z6sIqbhagwGEa5KlrmTEUXik1KzYgW6IxuMt1PRRpIq4CM48btyD61bKu4bJkurHE/s320/05-Reset-Windows11.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFexHuxRLxSPLXfW7qpNoa5xQeUIogmxiyVXPlDqej-47ZZuaPQX8Jx__yPhDck48xOageN72ldUwVSHtQ8nYUw4ugNo_ETKjNP6YEKoE8CvAJYfQo9ubS0gFA9vSpOp5i-QTgFed80m3gG_5Iyu5wkeDMu154yWzaaAf82PRWjkKNR97de03lFNDlfJsg/s1006/06-Reset-Windows11.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="663" data-original-width="1006" height="211" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFexHuxRLxSPLXfW7qpNoa5xQeUIogmxiyVXPlDqej-47ZZuaPQX8Jx__yPhDck48xOageN72ldUwVSHtQ8nYUw4ugNo_ETKjNP6YEKoE8CvAJYfQo9ubS0gFA9vSpOp5i-QTgFed80m3gG_5Iyu5wkeDMu154yWzaaAf82PRWjkKNR97de03lFNDlfJsg/s320/06-Reset-Windows11.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著會花一點時間，例如 20-30 分鐘後，筆電重開機，此台筆電就進入顯示 "正在重設這部電腦 10%" ，依筆電規格運算時間不同，在這台大概還要運行 40 分鐘：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6vv7GtVTS0GmcFIM8uAnSy4zb8t_GeOYtEf9FjLtg0MOkY0oMJE7VS_ehz980GHG2B7-WmicEMs1mkmgB2H5lFjYjRDC_keYlcNh5Kcwp2Os00WEMyRNPCeOIjMKi4r_2_p_d7kc9JmOt9_QLm-fHSJJy6AQxWZV7tUclPmJhKlsfm3rIxjg7kv2O_Bdp/s1436/01-Reset-Windows11.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="922" data-original-width="1436" height="205" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6vv7GtVTS0GmcFIM8uAnSy4zb8t_GeOYtEf9FjLtg0MOkY0oMJE7VS_ehz980GHG2B7-WmicEMs1mkmgB2H5lFjYjRDC_keYlcNh5Kcwp2Os00WEMyRNPCeOIjMKi4r_2_p_d7kc9JmOt9_QLm-fHSJJy6AQxWZV7tUclPmJhKlsfm3rIxjg7kv2O_Bdp/s320/01-Reset-Windows11.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;手上的筆電，還會問你要不要保留筆電相關 keys ，通常是推薦選 ESC 略過清除：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;A configuration change was requested to clear this computer's TPM (Trusted Platform Module).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;WARNING: Clearing erases information stored on the TPM.&lt;/div&gt;&lt;div&gt;You will lose all created keys and access to data encrypted by these keys.&lt;/div&gt;&lt;div&gt;Press [F9] to confirm, ESC to reject&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The system will restart.&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;詢問 Claude.ai 提供的資訊：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;這個訊息是關於 TPM (Trusted Platform Module，信賴平台模組) 的重要警告。讓我解釋一下：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;TPM 是電腦主機板上的一個安全晶片，主要用來：&lt;/div&gt;&lt;div&gt;1. 存儲加密金鑰&lt;/div&gt;&lt;div&gt;2. 保護 Windows Hello 的生物識別資料（如指紋）&lt;/div&gt;&lt;div&gt;3. 保護 BitLocker 硬碟加密的金鑰&lt;/div&gt;&lt;div&gt;4. 確保開機過程的安全性&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這個警告在告訴你：&lt;/div&gt;&lt;div&gt;- 系統正在請求清除 TPM 中的資料&lt;/div&gt;&lt;div&gt;- 如果你按下 F9 確認清除，你將會失去：&lt;/div&gt;&lt;div&gt;&amp;nbsp; - TPM 中儲存的所有加密金鑰&lt;/div&gt;&lt;div&gt;&amp;nbsp; - 使用這些金鑰加密的資料的存取權限&lt;/div&gt;&lt;div&gt;- 按 ESC 可以取消這個動作&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;建議：&lt;/div&gt;&lt;div&gt;1. 如果你正在重置電腦並打算重新開始使用，按 F9 沒有問題&lt;/div&gt;&lt;div&gt;2. 如果你的電腦有使用 BitLocker 加密，請確保你已經備份了重要資料&lt;/div&gt;&lt;div&gt;3. 如果你不確定要不要清除，建議先按 ESC，等確認沒有重要資料需要保留後再進行&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;清除後，系統會重新啟動，而 TPM 會重新初始化以供新的 Windows 安裝使用。&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著，就像筆電剛買回來初次啟動一樣，開始被問要怎樣登入系統。印象中只要一開始設定好連外網路後，由於 Windows 也都在推廣自家帳號跟雲端服務，選個人使用會一直卡在被要求註冊一個帳號或登入一個微軟帳號，這時可以使用 "為公司或學校設定"，在這種組織流程中可以像以前 Windows 7 等等，直接創建本機帳號：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;為公司或學校設定 -&amp;gt; 登入選項 -&amp;gt; 改為加入網域 -&amp;gt; 輸入&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkTByNRqp3Q9gb7ym9Te6g2dLKkV2Iy119SBF2_ffRHtIM4so7czPpJ11R3F1THb99dncbV-v63IV0N3C2Urd8WrkmNRRM3Gb07Z_HIEOls6XLBxhnuK2nPXgSPmI60U9TwCdcD5PlfQfIAl4jgRYve8MnuEnxgrHoiJgOczkAyPr3C8_roDbIEATO0-29/s1024/LocalAccount-01.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="576" data-original-width="1024" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkTByNRqp3Q9gb7ym9Te6g2dLKkV2Iy119SBF2_ffRHtIM4so7czPpJ11R3F1THb99dncbV-v63IV0N3C2Urd8WrkmNRRM3Gb07Z_HIEOls6XLBxhnuK2nPXgSPmI60U9TwCdcD5PlfQfIAl4jgRYve8MnuEnxgrHoiJgOczkAyPr3C8_roDbIEATO0-29/s320/LocalAccount-01.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhaANnSsX-Sa32uv_VtjUwkqXDzb3wWEOgJMDv-99j_ZP1hnt-ldTOrnZ0C6-mEOX9rPUUoQdM_SHqSLO2OAsTKpYynf8s2fU6dXACNF50W9Yl5Hd44E892t2Y64zizySngG_Tb412QFjAW-ZUR_JzK28UGDVAJpPZ9FW66UEM490iagRLrPoOTm6I0fL_/s1024/LocalAccount-02.png.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="576" data-original-width="1024" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhaANnSsX-Sa32uv_VtjUwkqXDzb3wWEOgJMDv-99j_ZP1hnt-ldTOrnZ0C6-mEOX9rPUUoQdM_SHqSLO2OAsTKpYynf8s2fU6dXACNF50W9Yl5Hd44E892t2Y64zizySngG_Tb412QFjAW-ZUR_JzK28UGDVAJpPZ9FW66UEM490iagRLrPoOTm6I0fL_/s320/LocalAccount-02.png.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiH7H1hMSXmWaddu8qRYeF1AdMk4aef2W4AcmIabvKLBe-XKvZEfnyXHZSet__jzMvSGEPxjN7xpdNW3J8qCf7rVq-daF1nrsedOoywMCEVrVQhOHkvRSEXvEnNc50R2yOp92-jkMxLJdrtR1day3SZUqMace-9jQF7egowUZEKiRJka5BaiGfu4WSszoL4/s1024/LocalAccount-03.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="576" data-original-width="1024" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiH7H1hMSXmWaddu8qRYeF1AdMk4aef2W4AcmIabvKLBe-XKvZEfnyXHZSet__jzMvSGEPxjN7xpdNW3J8qCf7rVq-daF1nrsedOoywMCEVrVQhOHkvRSEXvEnNc50R2yOp92-jkMxLJdrtR1day3SZUqMace-9jQF7egowUZEKiRJka5BaiGfu4WSszoL4/s320/LocalAccount-03.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0F4TcBZ2JbiWU78hBJz-0UzObissG1Ujw53frFjtyatwMc_oEkV7Ys69W58TSS8_FzDxJ3gpsH_NPrDs6vLNxH5hVxHjAbXrK2tvA6kinEUIgG2TQFjrvBdKUzEHEZj5yZYMNi5boAik4IkrXzWrkSmHKaJSMpQJCDSeJ2VXCJGhUC8nMfXNY80DPjqVN/s1024/LocalAccount-04.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="576" data-original-width="1024" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0F4TcBZ2JbiWU78hBJz-0UzObissG1Ujw53frFjtyatwMc_oEkV7Ys69W58TSS8_FzDxJ3gpsH_NPrDs6vLNxH5hVxHjAbXrK2tvA6kinEUIgG2TQFjrvBdKUzEHEZj5yZYMNi5boAik4IkrXzWrkSmHKaJSMpQJCDSeJ2VXCJGhUC8nMfXNY80DPjqVN/s320/LocalAccount-04.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著進入系統後，大概會繼續做：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;系統安全更新 -&amp;gt; 升級到最新&lt;/li&gt;&lt;li&gt;系統 -&amp;gt; 遠端桌面 -&amp;gt; 啟用&lt;/li&gt;&lt;li&gt;系統 -&amp;gt; 電源和電池&amp;nbsp;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;-&amp;gt; 電源&lt;/li&gt;&lt;ul&gt;&lt;li&gt;-&amp;gt; 螢幕與睡眠 -&amp;gt; 插電時，在下列時間後讓裝置進入睡眠狀態：永不&lt;br /&gt;&lt;/li&gt;&lt;li&gt;-&amp;gt; 電源模式：最佳效能&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;li&gt;關閉 筆電關閉就進入休眠&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Ctrl + R -&amp;gt; 輸入 control panel&lt;br /&gt;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;-&amp;gt; 控制台 -&amp;gt; 硬體與音效 -&amp;gt; 電源選項 -&amp;gt; 選擇蓋上螢幕時的行為&lt;/li&gt;&lt;ul&gt;&lt;li&gt;當我關閉筆記電腦螢幕時：一般電源 -&amp;gt; 不進行動做&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;li&gt;開始 選單 -&amp;gt; Microsoft Store&lt;/li&gt;&lt;ul&gt;&lt;li&gt;-&amp;gt; Python 3.12&lt;/li&gt;&lt;li&gt;-&amp;gt; Visual Studio Code&lt;br /&gt;&lt;/li&gt;&lt;li&gt;-&amp;gt; Brave Browser&lt;/li&gt;&lt;li&gt;-&amp;gt; Mozilla Firefox&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;啟用系統內建 OpenSSH Server&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;div&gt;Ctrl+R -&amp;gt; 輸入 services.msc -&amp;gt; 找尋 OpenSSH 伺服器&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;-&amp;gt; 按 右鍵 選 啟用&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;-&amp;gt; 按 右鍵 選 內容 -&amp;gt; 啟動類型 -&amp;gt; 自動&lt;/div&gt;&lt;/li&gt;&lt;li&gt;防火牆設定 -&amp;gt; Ctrl+R -&amp;gt; wf.msc -&amp;gt; 輸入規則 -&amp;gt; OpenSSH SSH Server (sshd)&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;-&amp;gt; 進階 -&amp;gt; 設定檔 -&amp;gt; 勾選 網域/私人/公用&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;啟用 telnet 工具&lt;/li&gt;&lt;ul&gt;&lt;li&gt;控制台 -&amp;gt; 程式集 -&amp;gt; 程式和功能 -&amp;gt; 開啟或關閉 Windows 功能 -&amp;gt; Telnet Client&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;下載 CygWin 安裝更多工具&lt;/li&gt;&lt;ul&gt;&lt;li&gt;https://www.cygwin.com/install.html&lt;/li&gt;&lt;ul&gt;&lt;li&gt;或是用 Windows 11 系統內建的 curl 來下載&lt;/li&gt;&lt;ul&gt;&lt;li&gt;C:\Users\User\Downloads&amp;gt;curl https://www.cygwin.com/setup-x86_64.exe &amp;gt; setup-x86_64.exe&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;li&gt;安裝 curl, wget, vim, git, cmake, clang, autossh 工具&lt;/li&gt;&lt;li&gt;之後透過 command 遠端登入 Windows 筆電時，可以在靠指令切換到 CygWin 的環境：&lt;/li&gt;&lt;ul&gt;&lt;li&gt;C:\Users\User&amp;gt;C:\cygwin64\Cygwin.bat&amp;nbsp;&lt;br /&gt;&lt;br /&gt;User@PC ~&lt;br /&gt;$&lt;br /&gt;&lt;br /&gt;User@PC ~&lt;br /&gt;$ pwd&lt;br /&gt;/cygdrive/c/Users/User&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;li&gt;使用 winget 指令：&lt;a href="https://learn.microsoft.com/zh-tw/windows/package-manager/winget/" rel="nofollow" target="_blank"&gt;learn.microsoft.com/zh-tw/windows/package-manager/winget/&lt;/a&gt;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;太久沒用 Windows ，沒想到 Microsoft 有推出自家牌 winget 套件管理工具，直接用他安裝工具&lt;/li&gt;&lt;li&gt;安裝完記得要重新登入才會更新工具搜尋環境，但是，有些工具裝完的確找不到，需自行更新 PATH 資訊&lt;/li&gt;&lt;li&gt;範例一：安裝 wget 指令&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;/li&gt;&lt;li&gt;C:\Users\User&amp;gt;wget&lt;/li&gt;&lt;li&gt;'wget' 不是內部或外部命令、可執行的程式或批次檔。&lt;/li&gt;&lt;li&gt;C:\Users\User&amp;gt;winget install wget&lt;/li&gt;&lt;li&gt;找到 Wget [JernejSimoncic.Wget] 版本 1.21.4&lt;/li&gt;&lt;li&gt;此應用程式已由其擁有者授權給您。&lt;/li&gt;&lt;li&gt;Microsoft 不負任何責任，也不會授與協力廠商封裝的任何授權。&lt;/li&gt;&lt;li&gt;正在下載 https://eternallybored.org/misc/wget/1.21.4/64/wget.exe&lt;/li&gt;&lt;li&gt;&amp;nbsp; ██████████████████████████████&amp;nbsp; 6.71 MB / 6.71 MB&lt;/li&gt;&lt;li&gt;已成功驗證安裝程式雜湊&lt;/li&gt;&lt;li&gt;正在啟動套件安裝...&lt;/li&gt;&lt;li&gt;新增的命令列別名: "wget"&lt;/li&gt;&lt;li&gt;已修改路徑環境變數；重新啟動命令介面以使用新值。&lt;/li&gt;&lt;li&gt;已成功安裝&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;範例二：安裝 git vim&lt;/li&gt;&lt;ul&gt;&lt;li&gt;C:\Users\User&amp;gt;winget install git vim&lt;/li&gt;&lt;li&gt;找到多個符合輸入條件的套件。請精煉輸入。&lt;/li&gt;&lt;li&gt;名稱&amp;nbsp; &amp;nbsp;識別碼&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 來源&lt;/li&gt;&lt;li&gt;----------------------------&lt;/li&gt;&lt;li&gt;My Git 9NLVK2SL2SSP&amp;nbsp; msstore&lt;/li&gt;&lt;li&gt;Git&amp;nbsp; &amp;nbsp; Git.Git&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;winget&lt;/li&gt;&lt;li&gt;Git&amp;nbsp; &amp;nbsp; Microsoft.Git winget&lt;/li&gt;&lt;li&gt;找到多個套件: git&lt;/li&gt;&lt;li&gt;找到多個符合輸入條件的套件。請精煉輸入。&lt;/li&gt;&lt;li&gt;名稱&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 識別碼&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 來源&lt;/li&gt;&lt;li&gt;---------------------------------------&lt;/li&gt;&lt;li&gt;Vim Cheat Sheet 9WZDNCRDMCWR&amp;nbsp; &amp;nbsp; msstore&lt;/li&gt;&lt;li&gt;Vim&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;vim.vim&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;winget&lt;/li&gt;&lt;li&gt;Vim&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;vim.vim.nightly winget&lt;/li&gt;&lt;li&gt;找到多個套件: vim&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;大概就安裝這些工具：&lt;/li&gt;&lt;ul&gt;&lt;li&gt;C:\Users\User&amp;gt;winget install wget vim.vim Microsoft.Git GnuWin32.Grep CoreyButler.NVMforWindows&amp;nbsp;Docker.DockerDesktop&lt;/li&gt;&lt;ul&gt;&lt;li&gt;其中 vim 預設還沒更新到 PATH 環境，需要去找他出來用 "C:\Program Files\Vim\vim91\vim.exe" ，對應的還有 "C:\Program Files (x86)\GnuWin32\bin\grep.exe" 等等&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/div&gt;如此，又變成可以遠端進去處理、測試的 Windows 節點了！ 像是 Python Windows 的實驗環境就可以用：&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"&gt;&lt;div style="text-align: left;"&gt;&lt;div&gt;C:\Users\User\Downloads&amp;gt;python -m venv venv&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;C:\Users\User\Downloads&amp;gt;.\venv\Scripts\activate&lt;/div&gt;&lt;div&gt;C:\Users\User\Downloads&amp;gt;pip install grip&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;</description><link>http://blog.changyy.org/2024/11/reset-reinstall-windows-11-os-notebb.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHZFy48tpHiv4NKguPueZlDcIxdnldv3AMn4ZrMQ4r53RBxDHuQE139cHqcDlnMZfXMQgXfFfIIzHG-NU99Bd_FLW8SCj4YPxPbf-ju5eq6EFinku15lJ9JDUOch8tWzkAcmPjVEEfI96nSjRW_EKZYWB95ScpWpSbc4IeyWR2uXziiyRsyF9keD8CzAGc/s72-c/02-Reset-Windows11.png" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-7272972227976009541</guid><pubDate>Tue, 29 Oct 2024 12:00:00 +0000</pubDate><atom:updated>2024-10-29T20:02:21.111+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">V-TEX</category><category domain="http://www.blogger.com/atom/ns#">優點</category><category domain="http://www.blogger.com/atom/ns#">滑倒</category><category domain="http://www.blogger.com/atom/ns#">缺點</category><category domain="http://www.blogger.com/atom/ns#">脫膠</category><category domain="http://www.blogger.com/atom/ns#">防水性極佳</category><category domain="http://www.blogger.com/atom/ns#">防水鞋</category><category domain="http://www.blogger.com/atom/ns#">鞋底設計</category><title>V-TEX防水休閒鞋 鞋底 滑 / 脫膠 使用心得</title><description>&lt;div&gt;源自於股癌2020年業配，那時初次嘗試了防水鞋，同年也在嘖嘖群眾募資嘗試過另一牌防水鞋，接著又買過傳統鞋廠 New Balance GORE-TEX 防水鞋，連我自己買的跟送家人穿的，近四年大概買超過 10 防水雙鞋了 XD 在 V-TEX 都買到鑽石會員...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;不得不說，V-TEX 防水效果最好，我自己猜測，應當是 V-TEX 的機構設計，例如降低鞋子摺痕，使得鞋面面對水滴時，防水性能就能長效維持，而其他款牌鞋子的防水性能一開始也是好的，但相對於 V-TEX 穿一年還能防水，其他家就容易不到半年的時間隨著鞋子穿久摺痕出現而防水性失效，我想，這應當就是 V-TEX 鞋面的設計優勢&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這也讓我開始推家人穿 V-TEX 防水休閒鞋，然而，產品多用後，也發現一些現象：V-TEX 的鞋底設計，有明顯劣勢。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;首先，可以在 V-TEX 官網去留意鞋底設計，應該有兩三款鞋底設計，其中一款是很防滑的，穿起來也很舒適，缺點就是容易脫膠，主要是他的鞋底製作技術的關係：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqrqKes6xdghDAp0A7OfEz-Roa-KFYAugC0ZaAji-XctUyg1lKbMD0WCWz0bqSfVAz9E03O1h9Cl6pGc7t6lIOO8cnW8SvfDTmYnta_x12G9Nu4SacaEfq6shGpa6juCRP3EAnLj8mX7w17g7nv8WLyv6jncBk2Wnmc7YXpl6aAPcGZ8faGo7oUvKAZGt4/s800/%E8%84%AB%E8%86%A0.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="485" data-original-width="800" height="388" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqrqKes6xdghDAp0A7OfEz-Roa-KFYAugC0ZaAji-XctUyg1lKbMD0WCWz0bqSfVAz9E03O1h9Cl6pGc7t6lIOO8cnW8SvfDTmYnta_x12G9Nu4SacaEfq6shGpa6juCRP3EAnLj8mX7w17g7nv8WLyv6jncBk2Wnmc7YXpl6aAPcGZ8faGo7oUvKAZGt4/w640-h388/%E8%84%AB%E8%86%A0.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;其他款鞋底是一體成型的，這款就不會脫膠，但缺點就是不防滑，只要是地面微濕的磁磚或是水溝蓋，走路必須分外小心：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKwW2y88lLTk68u3W7mpTf2n-Jz54Iin4MlZnxhFh0ZQXVMfhqTENvf7J11pb8sgnwozQVyPA2m8Bwcq0ard3Q3S6hwgk2ogQ76v4D1YeVlcMgAXw5ftCp7vdT1Gi6Sz3W_F_rCCSvvjtS7GxGEiYZHM2tDO69eR57JZSXF0LkZei-EjQN_T_VWMlLLcgA/s800/%E4%B8%8D%E9%98%B2%E6%BB%91.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="379" data-original-width="800" height="304" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKwW2y88lLTk68u3W7mpTf2n-Jz54Iin4MlZnxhFh0ZQXVMfhqTENvf7J11pb8sgnwozQVyPA2m8Bwcq0ard3Q3S6hwgk2ogQ76v4D1YeVlcMgAXw5ftCp7vdT1Gi6Sz3W_F_rCCSvvjtS7GxGEiYZHM2tDO69eR57JZSXF0LkZei-EjQN_T_VWMlLLcgA/w640-h304/%E4%B8%8D%E9%98%B2%E6%BB%91.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;大概基於這些使用心得，我近期應當不會買來送家人，穿一穿脫膠也不是三秒膠可以搞定的事，而另一款不防滑...連我自己近四年都有兩次完整滑倒到手撐地和數次滑倒且順利平衡穩住的經驗 Orz 查詢網路分享文都能輕鬆看到回饋，只能說公司肯定都知道，期待之後鞋底的部分可以再次改良，這防水技術是真的沒話說。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiujLdtDYpCNx1D5EYy9-yrysryswmYlLN2C-AXElbxTXfwtmDPCreysX-WD9nyRunaAs1jQENzU85vLgCGkYA3sA3SWDauYTEK1jXbkdVU87wdXzVmD64k6TEzWmt9pI7As-2i05xr7GsbyFKBmoAUM33V5NJFKs6Cc9SzVlLZhJAp_QkZyrfOyG0rmHJ/s2698/%E6%A5%AD%E9%85%8D-fb-%E9%AD%9A%E6%BC%BF%E5%A4%AB%E5%A9%A6.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="2060" data-original-width="2698" height="489" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiujLdtDYpCNx1D5EYy9-yrysryswmYlLN2C-AXElbxTXfwtmDPCreysX-WD9nyRunaAs1jQENzU85vLgCGkYA3sA3SWDauYTEK1jXbkdVU87wdXzVmD64k6TEzWmt9pI7As-2i05xr7GsbyFKBmoAUM33V5NJFKs6Cc9SzVlLZhJAp_QkZyrfOyG0rmHJ/w640-h489/%E6%A5%AD%E9%85%8D-fb-%E9%AD%9A%E6%BC%BF%E5%A4%AB%E5%A9%A6.png" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;ref:&amp;nbsp;&lt;a href="https://www.facebook.com/FPCChannel/posts/%E6%82%A8%E4%B8%8D%E4%B8%80%E5%AE%9A%E8%A6%81%E8%B2%B7v-tex%E9%98%B2%E6%B0%B4%E9%9E%8B%EF%B8%8F%E9%82%A3%E5%80%8B%E6%82%A8%E6%B2%92%E6%9C%89%E7%9C%8B%E9%8C%AF%E9%80%99%E4%B8%8D%E6%98%AFv-tex%E9%98%B2%E6%B0%B4%E9%9E%8B%E7%9A%84%E6%8E%A8%E5%BB%A3%E6%96%87%E7%AB%A0%E5%97%8E%E8%80%8C%E4%B8%94%E9%80%99%E6%AC%A1%E6%88%91%E4%B9%9F%E5%9C%A8%E6%97%A5%E6%9C%AC%E6%AB%BB%E8%8A%B1%E5%AD%A3%E5%89%8D%E7%9A%84%E6%9C%80%E5%BC%B7%E9%9B%A8%E5%AD%A3%E8%B8%A9%E6%B0%B4%E5%AF%A6%E7%A9%BF%E9%82%A3%E9%BA%BC%E7%82%BA%E4%BB%80%E9%BA%BC%E6%88%91%E6%9C%83%E9%80%99%E9%BA%BC%E8%AA%AA%E5%91%A2%E5%85%B6%E5%AF%A6%E6%88%91%E4%BB%94%E7%B4%B0%E5%9C%A8%E7%B6%B2%E8%B7%AF/975925437237311/" rel="nofollow" target="_blank"&gt;FB POST&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;最後一提，碰到脫膠都可以與客服尋求處理方式，售後服務是很不錯的，且 V-TEX 穿超過一年還是防水，只剩鞋底磨損，若有替換鞋底的功能，就更環保了。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8vV77nAoXJb6VmQs6kF8TGcBx5V9FQduo4ScWTHDpUrvckfHFRnhllAxODdMUWMtCANdp2boubmbd1OFHhmn0JXck1ckKDD5OWI3vqhHUK2rKqM9ehQCNJC9zG8wueegKtaI9mOG3-KHIlukl4IaP7gHGrGRIV49ip1Ool63onl4H92atATXWJX1_CKYF/s800/test.jpeg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="479" data-original-width="800" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8vV77nAoXJb6VmQs6kF8TGcBx5V9FQduo4ScWTHDpUrvckfHFRnhllAxODdMUWMtCANdp2boubmbd1OFHhmn0JXck1ckKDD5OWI3vqhHUK2rKqM9ehQCNJC9zG8wueegKtaI9mOG3-KHIlukl4IaP7gHGrGRIV49ip1Ool63onl4H92atATXWJX1_CKYF/w400-h240/test.jpeg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;近四年至少滿滿三年都穿這牌子，打從心底希望這品牌可以把鞋底改良，真的是不錯的鞋。&lt;/div&gt;</description><link>http://blog.changyy.org/2024/10/v-tex-advantages-and-disadvantages.html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqrqKes6xdghDAp0A7OfEz-Roa-KFYAugC0ZaAji-XctUyg1lKbMD0WCWz0bqSfVAz9E03O1h9Cl6pGc7t6lIOO8cnW8SvfDTmYnta_x12G9Nu4SacaEfq6shGpa6juCRP3EAnLj8mX7w17g7nv8WLyv6jncBk2Wnmc7YXpl6aAPcGZ8faGo7oUvKAZGt4/s72-w640-h388-c/%E8%84%AB%E8%86%A0.jpg" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-5580186169894889967</guid><pubDate>Mon, 14 Oct 2024 14:10:00 +0000</pubDate><atom:updated>2024-10-14T22:10:00.111+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">24.04</category><category domain="http://www.blogger.com/atom/ns#">docker</category><category domain="http://www.blogger.com/atom/ns#">install</category><category domain="http://www.blogger.com/atom/ns#">ubuntu16.04</category><title>Linux 開發筆記 - 在 Ubuntu 16.04 使用 Docker Ubuntu 24.04 無法正常更新套件 (apt update）@ Ubuntu 16.04</title><description>追蹤到最後，解法應當是把 Host 端的 docker 更新來修正，但一開始很懶，只好遮住雙眼摸魚一下 XD&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;狀態：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;$ lsb_release -a&lt;/div&gt;&lt;div&gt;No LSB modules are available.&lt;/div&gt;&lt;div&gt;Distributor ID:&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;Ubuntu&lt;/div&gt;&lt;div&gt;Description:&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;Ubuntu 16.04 LTS&lt;/div&gt;&lt;div&gt;Release:&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;16.04&lt;/div&gt;&lt;div&gt;Codename:&lt;span style="white-space: pre;"&gt;	&lt;/span&gt;xenial&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;$ docker run -it ubuntu:24.04 /bin/bash&lt;/div&gt;&lt;div&gt;root@a0f5c64ed667:/# apt update&lt;/div&gt;&lt;div&gt;Get:1 http://security.ubuntu.com/ubuntu noble-security InRelease [126 kB]&lt;/div&gt;&lt;div&gt;Get:2 http://archive.ubuntu.com/ubuntu noble InRelease [256 kB]&lt;/div&gt;&lt;div&gt;Err:1 http://security.ubuntu.com/ubuntu noble-security InRelease&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 871920D1991BC93C&lt;/div&gt;&lt;div&gt;Err:2 http://archive.ubuntu.com/ubuntu noble InRelease&lt;/div&gt;&lt;div&gt;&amp;nbsp; The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 871920D1991BC93C&lt;/div&gt;&lt;div&gt;Get:3 http://archive.ubuntu.com/ubuntu noble-updates InRelease [126 kB]&lt;/div&gt;&lt;div&gt;Err:3 http://archive.ubuntu.com/ubuntu noble-updates InRelease&lt;/div&gt;&lt;div&gt;&amp;nbsp; The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 871920D1991BC93C&lt;/div&gt;&lt;div&gt;Get:4 http://archive.ubuntu.com/ubuntu noble-backports InRelease [126 kB]&lt;/div&gt;&lt;div&gt;Err:4 http://archive.ubuntu.com/ubuntu noble-backports InRelease&lt;/div&gt;&lt;div&gt;&amp;nbsp; The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 871920D1991BC93C&lt;/div&gt;&lt;div&gt;Reading package lists... Done&lt;/div&gt;&lt;div&gt;W: GPG error: http://security.ubuntu.com/ubuntu noble-security InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 871920D1991BC93C&lt;/div&gt;&lt;div&gt;E: The repository 'http://security.ubuntu.com/ubuntu noble-security InRelease' is not signed.&lt;/div&gt;&lt;div&gt;N: Updating from such a repository can't be done securely, and is therefore disabled by default.&lt;/div&gt;&lt;div&gt;N: See apt-secure(8) manpage for repository creation and user configuration details.&lt;/div&gt;&lt;div&gt;W: GPG error: http://archive.ubuntu.com/ubuntu noble InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 871920D1991BC93C&lt;/div&gt;&lt;div&gt;E: The repository 'http://archive.ubuntu.com/ubuntu noble InRelease' is not signed.&lt;/div&gt;&lt;div&gt;N: Updating from such a repository can't be done securely, and is therefore disabled by default.&lt;/div&gt;&lt;div&gt;N: See apt-secure(8) manpage for repository creation and user configuration details.&lt;/div&gt;&lt;div&gt;W: GPG error: http://archive.ubuntu.com/ubuntu noble-updates InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 871920D1991BC93C&lt;/div&gt;&lt;div&gt;E: The repository 'http://archive.ubuntu.com/ubuntu noble-updates InRelease' is not signed.&lt;/div&gt;&lt;div&gt;N: Updating from such a repository can't be done securely, and is therefore disabled by default.&lt;/div&gt;&lt;div&gt;N: See apt-secure(8) manpage for repository creation and user configuration details.&lt;/div&gt;&lt;div&gt;W: GPG error: http://archive.ubuntu.com/ubuntu noble-backports InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 871920D1991BC93C&lt;/div&gt;&lt;div&gt;E: The repository 'http://archive.ubuntu.com/ubuntu noble-backports InRelease' is not signed.&lt;/div&gt;&lt;div&gt;N: Updating from such a repository can't be done securely, and is therefore disabled by default.&lt;/div&gt;&lt;div&gt;N: See apt-secure(8) manpage for repository creation and user configuration details.&lt;/div&gt;&lt;div&gt;E: Problem executing scripts APT::Update::Post-Invoke 'rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true'&lt;/div&gt;&lt;div&gt;E: Sub-process returned an error code&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;硬解：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;root@a0f5c64ed667:/# apt update --allow-insecure-repositories&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;安裝軟體：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"&gt;&lt;div&gt;```&lt;/div&gt;&lt;div&gt;root@a0f5c64ed667:/# apt install -y --allow-unauthenticated vim wget curl git tree python3-virtualenv&amp;nbsp;&lt;/div&gt;&lt;div&gt;...&lt;/div&gt;&lt;div&gt;```&lt;/div&gt;&lt;/blockquote&gt;&lt;p&gt;結果越裝越痛苦 XD 還是回去看 Docker 官網安裝教學：&lt;a href="https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository" rel="nofollow" target="_blank"&gt;docs.docker.com/engine/install/ubuntu/#install-using-the-repository&lt;/a&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;如此就可以在 Host Ubuntu 16.04 ，正確呼喚出 docker Ubuntu 24.04 來運行，此外，也可以留意 Docker 官方文件建議的 Ubuntu OS 環境已經是 20.04 以上了&lt;/p&gt;</description><link>http://blog.changyy.org/2024/10/linux-ubuntu-1604-run-docker-ubuntu-2404.html</link><author>noreply@blogger.com (Unknown)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4654305895942962500.post-50047647086550119</guid><pubDate>Tue, 08 Oct 2024 16:44:00 +0000</pubDate><atom:updated>2024-10-14T23:48:38.751+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">google cloud ai study jam</category><title>學習完畢 Google Cloud AI Study Jam 2024 - 生成式 AI 培訓計劃</title><description>&lt;div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYdPDriK_eUnL7eWeu0dyAI6t8Iz7_jJR_SzwYz7JtfmkFbemxHGZPZBr1IwGpbzylmel-mdADiB9daQuNY4HcjoFWKU0pxUiz_jVlY6j6iu2s9g4ro32Ad8y_zCickoMRhZXWjwGo5HezHjishIRfXaERAdG9bpu4vhuvAsHCWwIggdkYPn5ipHcAiL_L/s1872/%E6%88%AA%E5%9C%96%202024-10-08%2023.06.05.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="854" data-original-width="1872" height="146" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYdPDriK_eUnL7eWeu0dyAI6t8Iz7_jJR_SzwYz7JtfmkFbemxHGZPZBr1IwGpbzylmel-mdADiB9daQuNY4HcjoFWKU0pxUiz_jVlY6j6iu2s9g4ro32Ad8y_zCickoMRhZXWjwGo5HezHjishIRfXaERAdG9bpu4vhuvAsHCWwIggdkYPn5ipHcAiL_L/s320/%E6%88%AA%E5%9C%96%202024-10-08%2023.06.05.png" width="320" /&gt;&lt;/a&gt;&lt;br /&gt;圖：&lt;a href="http://www.credly.com/users/changyy/" rel="nofollow" target="_blank"&gt;www.credly.com/users/changyy/&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;就這樣一口氣把 Google Cloud AI Study Jam 2024 都扛過去了，從月初到 10/08 晚間，每天多少都有清幾則：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;&lt;a href="https://www.cloudskillsboost.google/paths/118"&gt;Path 1: Beginner: Introduction to Generative AI Learning Path&lt;/a&gt; (5 堂課程)&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.cloudskillsboost.google/paths/236"&gt;Path 2: Intermediate: Gemini for Google Cloud Learning Path&lt;/a&gt; (8 堂課程)&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.cloudskillsboost.google/paths/183"&gt;Path 3: Advanced: Generative AI for Developers Learning Path&lt;/a&gt; (12 堂課程)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;原本是颱風天體驗了 Path 1 ，結果就這樣馬不停蹄地把 Path 2 跟 Path 3 也給 KO 了，真的獲益良多。&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;近一年當中，在 AI 應用開發跟自修上，剛好上過台大李宏毅教授的 Youtube 影片：&lt;a href="https://www.youtube.com/playlist?list=PLJV_el3uVTsPz6CTopeRp2L2t4aL_KgiI" rel="nofollow" target="_blank"&gt;【生成式AI導論 2024】&lt;/a&gt;，接著也協助公司使用 OpenAI API 建置了幾個雛形和一款稍微正式的對內服務，體驗過訓練自己的 model 來做客製化應用，以及也摸索過 &lt;a href="https://blog.changyy.org/2024/06/ai-openai-api-embedding-meta-facebook-search-faiss-cosine-similarity.html" rel="nofollow" target="_blank"&gt;RAG&lt;/a&gt;&amp;nbsp;應用和&lt;a href="https://blog.changyy.org/2024/03/ai-copilot-general-usage-designer-and-programmer-chatgpt-stable-diffusion-github-copilot.html" rel="nofollow" target="_blank"&gt;負責協助公司導入 AI 輔助應用&lt;/a&gt;等等，在這樣的背景下，進行 Path 1, Path 2 和 Path 3 仍收穫滿滿，補了不少思維：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;對 Google AI 的發展更新為沒脫隊！只是入門門檻高 XD&amp;nbsp;&lt;/li&gt;&lt;li&gt;研發 AI 服務時，要想想怎樣達成開發出一個負責任的 AI&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;在 Path 1：Beginner: Introduction to Generative AI Learning Path 過程時，開始熟悉 Google Cloud 的介面，儘管 2018 年左右就開始用 GCP ，但基於工作方式大多透靠 ansible + jenkins 完成任務，一直漠視了 Google 發展，這次使用上反而驚艷了一下，特別是 Gemini 小幫手到處都可以協助，不熟也沒關係，問！就對了。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在 Path 1 的課程就被 Google Cloud 網頁管理介面驚艷到，接著補了不少關於 負責任 AI 的思維，令人想起前陣子 &lt;a href="https://openai.com/index/introducing-superalignment/" rel="nofollow" target="_blank"&gt;OpenAI&amp;nbsp;Superalignment&lt;/a&gt; 的事情，雖然有點直覺，但對於事件的感受還不夠強烈，在上完課後終於能比較全面體會了，好在 OpenAI 在 &lt;a href="https://www.ithome.com.tw/news/163166" rel="nofollow" target="_blank"&gt;2024.05 成立新的安全委員會&lt;/a&gt;了。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在 Path 2: Intermediate: Gemini for Google Cloud Learning Path 課程，讓我感受到身為 RD / DevOps / SRE 時 AI 的靈壓，可以有以下體驗思維：&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;站在資方角度，不需聘請有高深經驗者，僅需找資淺的聰明者(學習力強)，只要能問對問題，靠 Gemini 補完工作，甚至快速累積經驗&lt;/li&gt;&lt;li&gt;站在開發者進行 side project 角度，透過 Gemini 大方地享受一人工作模式，可以只專精或感興趣一個領域，剩下的靠 Gemini 模擬出 10-30 人力規模的新創組織，幫你把點子完善到成品&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;此外，在 Path 2 的各種角色使用 Gemini 工作的思維，讓人欽佩大公司的組織架構，在小公司都是全包了 Orz&lt;/div&gt;&lt;/div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;Gemini for Application Developers&lt;/li&gt;&lt;li&gt;Gemini for Cloud Architects&lt;/li&gt;&lt;li&gt;Gemini for Data Scientists and Analysts&amp;nbsp;&lt;/li&gt;&lt;li&gt;Gemini for Network Engineers&lt;/li&gt;&lt;li&gt;Gemini for Security Engineers&lt;/li&gt;&lt;li&gt;Gemini for DevOps Engineers&lt;/li&gt;&lt;li&gt;Gemini for end-to-end SDLC&lt;/li&gt;&lt;li&gt;Develop GenAI Apps with Gemini and Streamlit&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;div&gt;在&amp;nbsp;Path 3: Advanced: Generative AI for Developers Learning Path 的心得，就進入比較正式的原理課程，雖然看過過台大教授的 AI 導論，但還是在這波習得不少底層運作的觀念。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnZavMwlrm3drnym83zJuZSZ64odx0TXhW69sZjc0XWje83VGW7LoClr5W0Qy3OUDfKpjgH_eeYhlKo4TdRVlYWeTExzxWLktbJ5TEsDF54F0vL6rra11zC8MCKMLzhdBw2mQN1zaEmwoQWfBoi_tZHGUw2fbpvpQbprovMaTmm7UaL_VF4-SCRlPUgN3M/s2084/%E6%88%AA%E5%9C%96%202024-10-08%2023.15.12.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1208" data-original-width="2084" height="370" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnZavMwlrm3drnym83zJuZSZ64odx0TXhW69sZjc0XWje83VGW7LoClr5W0Qy3OUDfKpjgH_eeYhlKo4TdRVlYWeTExzxWLktbJ5TEsDF54F0vL6rra11zC8MCKMLzhdBw2mQN1zaEmwoQWfBoi_tZHGUw2fbpvpQbprovMaTmm7UaL_VF4-SCRlPUgN3M/w640-h370/%E6%88%AA%E5%9C%96%202024-10-08%2023.15.12.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;圖：&lt;a href="https://www.cloudskillsboost.google/profile/leaderboard" rel="nofollow" target="_blank"&gt;www.cloudskillsboost.google/profile/leaderboard&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN7jibdfUiPVUq_IV0wcNkph2oJiV2v6Qr2X9FrrCqMvAmm0l622IrZ-cig_AdM4ObzAIFmNVEfURcBTZpSjnUgZEbg1f1EgZzVa_7V00vZTBRy2a_Wwpw8A1lMewO__uExLMutylOsOZhf59DTbRjkxYVFCXVyOoFI6eq8eOFf8olsHrfP2T3hFwNR_Af/s2346/%E6%88%AA%E5%9C%96%202024-10-08%2023.17.13.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1214" data-original-width="2346" height="332" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN7jibdfUiPVUq_IV0wcNkph2oJiV2v6Qr2X9FrrCqMvAmm0l622IrZ-cig_AdM4ObzAIFmNVEfURcBTZpSjnUgZEbg1f1EgZzVa_7V00vZTBRy2a_Wwpw8A1lMewO__uExLMutylOsOZhf59DTbRjkxYVFCXVyOoFI6eq8eOFf8olsHrfP2T3hFwNR_Af/w640-h332/%E6%88%AA%E5%9C%96%202024-10-08%2023.17.13.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;圖：&lt;a href="https://www.cloudskillsboost.google/paths" rel="nofollow" target="_blank"&gt;www.cloudskillsboost.google/paths&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;最後，差不多累積了兩萬分，其中 Quiz 錯了幾次，Lab 都一次通過。&lt;/div&gt;&lt;/div&gt;</description><link>http://blog.changyy.org/2024/10/google-cloud-ai-study-jam-2024..html</link><author>noreply@blogger.com (Unknown)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYdPDriK_eUnL7eWeu0dyAI6t8Iz7_jJR_SzwYz7JtfmkFbemxHGZPZBr1IwGpbzylmel-mdADiB9daQuNY4HcjoFWKU0pxUiz_jVlY6j6iu2s9g4ro32Ad8y_zCickoMRhZXWjwGo5HezHjishIRfXaERAdG9bpu4vhuvAsHCWwIggdkYPn5ipHcAiL_L/s72-c/%E6%88%AA%E5%9C%96%202024-10-08%2023.06.05.png" width="72"/><thr:total>0</thr:total></item></channel></rss>