分享一些Rust的便利小套件

自從住在日本之後,開始嘗試參加一些當地程序員的社群活動,其中最有參與感的就是Rust Ladies!
跟程度差不多的人一起學習和研究Rust的感覺真的很好,於是也嘗試更進一步地用Rust寫些Side Project了(笑)

在學習過程中發現Rust也有一些挺好用的套件和工具,於是在此分享一下:

1. dotenv
在開發的時侯,雖然設定檔用JSON或者ini會是最直觀和快捷的方法,
但如果日後想要在Docker環境下執行程序的話,採用.env 或者環境變數來輸入設定,還是會更為方便一些。
而為了讓Rust程序可以讀取.env檔案,可以使用dotenv這個套件:
https://crates.io/crates/dotenv


extern crate dotenv;
use dotenv::dotenv;
fn main() {
dotenv().ok();
}

安裝後,只要像以上的官方範例那樣使用dotenv()這個Method,就能自動讀取.env檔案的內容,當成普通的環境變數使用了。超方便的!

2. cargo-watch
不管是哪種程式語言,總會遇到每次修改完之後都得重新加載,浪費時間的問題…Rust當然也不例外。
而且Rust跟JS和PHP不同,在運行之前是需要編譯的;這就更加費時間了。
不過只要有cargo-watch,就能每次修改完程序文件之後自動重新編譯和運行,能夠省下許多功夫啦!
https://crates.io/crates/cargo-watch

在一般用cargo run來運行Rust程式的情況下,可以用以下指令來達到以上提到的效果:
cargo watch -- cargo run

但從官方文檔看來,應該是還有很多種用法….不過大概需要慢慢研究了。

這篇文章也應該會有後續更新,總之現在先以用Rust寫出一個Telegram機器人為目標!

利用Neutralino和Vue輕鬆開發跨平台桌面應用程式

Neutralino是什麼?

自從有了Electron後,以往許多只在Windows上推出桌面應用程式的軟體都紛紛推出Mac和Linux版,讓在這兩個平台上工作變得簡單和舒服了許多;這都是多虧了它的跨平台性所賜。

大家常用的Vscode Studio,也是利用它開發出來的。

然而Electron有一個讓人恨到極點的缺點:那就是它會將網頁(應用程式內容)和顯示內容用的瀏覽器(Chromium)打包在一起,也就讓每一個Electron應用都胖得要死!於是雖然它能讓開發者輕鬆開發跨平台應用,也能利用HTML + JS + CSS 如同寫網頁那般隨意地製作GUI,但也遂漸被重視性能和輕巧的人們敬為遠之。而針對這個問題誕生的,正是Electron的替代品--Neutralino。

Neutralino是一款主打輕量化的桌面應用開發框架,它在功能上基本跟Electron差不多,但不會像前者那樣把Chromium與應用打包在一起,而是會使用系統內建的瀏覽器內核(例如在Windows上會使用Edge)來渲染內容,如此一來就能保證速度之餘達到輕量化的效果。(關於Neutralino和其他同類框架之間的性能比較,請參考這裡 。)

由於Neutralino同時支援純Javascript和前端框架開發,本篇文章主要講述如何利用NeutralinoJS和VueJS,構建屬於自己的跨平台桌面應用程式。

1. 安裝Neutralino的命令行工具:
npm install -g @neutralinojs/neu

2. 運行前面安裝的命令行工具,建立Neutralinojs的空白模板:

neu create [專案名稱] --template neutralinojs/neutralinojs-zero

3. 進入資料夾,移除空白模板內的網頁內容:
cd ./[專案名稱]
rf www

4. 建立存放Vue專案的資料夾替代之,並將專案命名為”vue-src”:
npm create vue@latest

5. 編輯neutralino.config.json,修改Neutralino的設定內容。
主要需要修改的有圖中”title”,”documentRoot”, “resourcesPath”,”clientLibrary”和”icon”的部份:
“title” 是你的應用程式名稱,”documentRoot” 是存在應用內容的地方,因此需要改成”/vue-src/dist”,讓Neutralino讀取已經Build好的Vue專案內容;”resourcesPath”也是同理。
”clientLibrary”則是指Neutralino API庫的位置(我選擇放在/vue-src/public/內)。
雖然沒有它一切也能正常運作,但就會無法存取像是儲存檔案,開啟檔案等API,也就無法像一個真正的本地應用那樣工作了。
至於”icon”,自然就是你的應用ICON路徑了。由於剛開始應該沒有專用的icon,可以先用Vue預設的Favicon(/vue-src/public/favicon.ico)代替。
最終設定應該會像這樣:
6. 下載Neutralino API庫:

neu update

7.  構建Vue專案內容:
cd ./vue-src
npm install
npm run build

8. 回到專案頂層資料夾,運行Neutralino:
cd ..
neu run

登登!大功告成!之後的開發基本上跟平常的Vue應用差不多,但如果需要用到本地應用的API(例如儲存檔案,打開檔案等),就需要參考官方文檔了。

更完整的例子可以參考我寫的這個簡易To-Do-List應用:

https://github.com/falconshark/Neutralino-To-Do/

祝各位開發愉快!

Drupal 8 在/admin/config頁面下新增分類

首先在自訂模組的links.menu.yml(例如your_custom_module.links.menu.yml)下新增以下內容:

system.admin_config_new:
title: 'My New Config'
route_name: system.admin_config_new
parent: system.admin_config

然後再在routing.yml(例如your_custom_module.routing.yml)新增內容:

system.admin_config_new:
  path: '/admin/config/new'
  defaults:
    _controller: '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
    _title: 'My New Config'
  requirements:
    _permission: 'access administration pages'

清除緩存後即會生效。

以上代碼中請將自行將route名改為想要的名字,path和title同理。

Node Sass Watch不能正常使用的暫時代替方案

不知為何自從node-sass升級後,-w的選項就一直沒有生效過--能執行成功,但無法偵測指定Sass/Scss檔案的修改,這樣在無法使用IDE(例如在伺服器作業)的情況下編譯檔案會變得有點麻煩。

目前想到的解決方法是改用Gulp 監視檔案修改,有點麻煩但最少有效。

  1. 安裝gulp和gulp-sass:
npm install gulp gulp-sass
yarn add gulp gulp-sass

2.創建gulpfile.js,輸入以下內容:

vi gulpfile.js

const gulp = require('gulp');
const sass = require('gulp-sass');

gulp.task('watch', function () {
  gulp.watch('./css/*.scss', gulp.series('build:scss'));
});

gulp.task('build:scss', function() {
    return gulp.src('./css/style.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('./css/'));
});

3.在Package.json加入Watch指令:

  "scripts": {
    "watch": "./node_modules/.bin/gulp watch"
  }

4.想要編譯Sass/Scss檔案時,運行Watch指令:

npm run watch
yarn watch

完成。逃避可恥但有用,對吧?

如何在使用Let’s Encrypt + Nginx 重定向的情況下避免CORS問題?

用Fetch抓取和傳輸資料雖然很方便,也遇上CORS問題的時侯就笑不出來了。查資料加上除錯了一整晚,最後終於知道要如何在API 使用Let’s Encrypt重定向的時侯到底要怎樣避免CORS問題。

首先,要在後端程式加上Access-Control-Allow-Origin的Header。這應該大家都知道了,但還是提醒一下這個是基礎第一步喔。以PHP為例子:

header("Access-Control-Allow-Origin: *");

之後你可能會發現:咦?為什麼我設定了Access-Control-Allow-Origin,還是會出現同樣的問題呢?原因非常簡單:CORS不接受2xx以外的Staus Code回應!

用Fetch POST JSON時,瀏覽器會先發出OPTION的請求。這時Nginx如果不好好處理,就會給出405回應,導致問題發生。

所以我們要在Nginx的設定裡加上這個:

location / {  
    if ($request_method = 'OPTIONS') {
        return 204;
    }
} 

重啟Nginx後就能解決問題了。

順便提醒一下,前端的Code也需要注意在CROS模式下Fetch的時侯不能帶Header,否則一樣會出現問題喔。嚶嚶嚶。

  fetch(apiUrl, {
      body:JSON.stringify({
        width: 500,
        height: 300,
        imageUrl: imgUrl,
      }),
      mode: 'cors',
      method: 'POST',
    })

鳴謝Front-End Developers Taiwan翁傳翔林坤南先生指出文中問題!

如何讓遠端伺服器使用自己電腦上的SSH Key?

習慣用Git在伺服器上部署網站後,已經不想再用FTP或者SFTP這麼老套的方法來做事了。

不過如果想用SSH的方法訪問Git,很多時侯我們需要先為伺服器準備獨立的KEY,這樣實在有點麻煩。其實最好的方法是使用SSH Agent Forwarding讓遠端伺服器也能使用你電腦原來的金鑰,做法也非常簡單。

首先,新建/修改你電腦上的~/.ssh/config檔案 ,新增以下內容:

Host your_server
HostName server.com
user root
ForwardAgent yes

Host是你設定的伺服器名稱,請隨意修改。HostName則是你的伺服器網址/IP,user是指定的伺服器用戶,至於ForwardAgent就是本次的重點,請務必設定為yes。

完成後,輸入以下指令檢查目前Agent是否正使用你的Key:

ssh-add -L

否則輸入指令:

ssh-add -K

完成後再次登入你的伺服器:(請使用之前設定的伺服器名稱登入,否則不會生效)

ssh your_server

想知道更多關於ForwardAgent的資料,可以查看以下參考網站:

SSH agent forwarding 的應用

Javascript 如何比對Object是否相同(Equal)

在沒有Key或者Id的情況下,比對Object真的是世界上最噁心的一件事情了。

最簡單的方法是這個:

const object1 = {"Name": "Hello"};
const object2 = {"Name": "Hello"};

if(JSON.stringify(object1) === JSON.stringify(object2)){
  return true;
}

很簡單對吧?然而世界沒有那麼美好。更多時侯你會遇到這種情況:

const object1 = {"Sex": "Girl", "Name": "Hello"};
const object2 = {"Name": "Hello", "Sex": "Girl"};

這種時侯用上一種方法是會Return False,失敗的。手寫排序Function是一種處理方法,但工時不足的情況下可以考慮用Sort Object這個模組解決問題:

https://github.com/doowb/sort-object
const sortObj = require('sort-object');
const object1 = sortObj({"Sex": "Girl", "Name": "Hello"});
const object2 = sortObj({"Name": "Hello", "Sex": "Girl"});

if(JSON.stringify(object1) === JSON.stringify(object2)){
  return true;
}

好了,輕鬆搞定。如果遇到更複雜的情況,需要深度比對的話,那就需要另一個名為Deep Equal的模組了。

https://github.com/substack/node-deep-equal
const sortObj = require('sort-object');
const equal = require('deep-equal');
const object1 = sortObj({"Sex": "Girl", "Name": "Hello"});
const object2 = sortObj({"Name": "Hello", "Sex": "Girl"});

if(equal(object1) === equal(object2)){
  return true;
}

過於依靠他人的模組是不好的事,但這是最簡單和最快的方法了。有空的時侯再研究一下怎麼手寫出同樣的功能吧!

◢▆▅▄▃崩╰(〒皿〒)╯潰▃▄▅▇◣

Mastodon Docker 更新版本教簡易教學

要用Docker架設Mastodon並不困難,要升級更加不難,但當初官方寫的教學文檔好像已經很久沒更新了,所以還是寫一寫吧。

要升級Docker版Mastodon,首先要進入你的Mastodon資料夾,運行以下指令:

cd <你的Mastodon資料夾路徑>
git fetch && git checkout <目前最新版本,例如v2.7.3>

Git Checkout能讓你的Mastodon代碼馬上更新到指定版本。但Docker裡的代碼並不會隨之更新,所以我們還需要用Docker Compose重建Image,最後重啟Container即可:

docker-compose build
docker-compose up -d

一般來說,到了這個步驟一切就已經大公告成。可是某些比較大型的更新需要更新資料庫或其他額外步驟,建議參閱Mastodon更新日誌裡的Upgrade notes。

https://github.com/tootsuite/mastodon/releases

WordPress get_posts() Function 排除特定分類的方法

WordPress 的get_posts() Function 對於Plugin或者Theme 開發者來說都很有用:相對上沒有WP_Query那麼複雜,但同樣支援WP_Query應有的功能。但是,Wordpress 的API文檔上好像沒有提到這點哪?

事實上,get_posts的參數是跟WP_Query共用的。也就是說,如果你想在使用get_posts()搜尋文章時排除特定分類,只要這樣就好了:

  $posts = get_posts(array(
    'category__not_in' => array(76), //此處必須為分類ID(TAG ID),不可使用SLUG
  ));

看,很簡單對吧?

不過,果然我還是比較喜歡Drupal …..

參考資料:

exclude category from get_posts?

停止Cisco AnyConnect Secure Mobility Client 開機時自動啟動的方法

Cisco AnyConnect Client每次當我打開Mac的時侯總會自己跳出來,偏偏因為工作因素又非裝不可,只好找點方法取消它了。

在網絡上查到的方法如下:

cd /Library/LaunchAgents
sudo rm com.cisco.anyconnect.*

原理是在LaunchAgents資料夾裡把Anyconnect的部份刪掉,理論上之後重新開機的時侯就不會再讓它跳出來了。

好一個令人頭疼的軟體。

參考資料:

Stop Cisco AnyConnect Secure Mobility Client from starting up automatically