ホームページ > バックエンド開発 > Golang > シンプルなパスワード マネージャー デスクトップ アプリ: Golang の Wails フレームワークへの進出 (パート 2)

シンプルなパスワード マネージャー デスクトップ アプリ: Golang の Wails フレームワークへの進出 (パート 2)

Susan Sarandon
リリース: 2024-12-30 09:50:16
オリジナル
495 人が閲覧しました

プログラマーの皆さん、こんにちは!この短いシリーズの最初の部分では、Wails フレームワークで作成したパスワードを保存および暗号化するためのデスクトップ アプリケーションの作成と操作について説明しました。また、Go バックエンドとそれをフロントエンド側にバインドする方法についても説明しました。

このパートでは、ユーザー インターフェイスを扱います。その投稿で述べたように、Wails を使用すると、Vanilla JS を含む任意の Web フレームワークを使用して GUI を構築できます。先ほども言いましたが、Wails の作成者は常に Svelte を第一候補として挙げているため、Svelte を気に入っているようです。 Wails CLI (現在のバージョン) は、Svelte Typescript (wails init -n myproject -t svelte-ts) でプロジェクトを作成するように要求すると、Svelte3 でスキャフォールディングを生成します。すでにお話ししたように、Svelte5 (およびその新機能) を使用したい場合は、その作成を自動化する bash スクリプトがあります (いずれの場合も、Wails CLI をインストールする必要があります)。さらに、Taildwindcss Daisyui が追加されており、インターフェースのデザインに最適な組み合わせであると思われます。

実のところ、私は最初に Vanilla JsVue を使用し、次に React を使用しました。さらに、多くの人にとって HTMX (これは大好きだと言わざるを得ません ❤️)。しかし、Svelte は最初から恋に落ちます。私が初めてこれを使用したのは、Wails を試しているときだったと言わざるを得ません (そして、これからも使い続けることを約束します…)。しかし、Web フレームワークは快適ですが、フロントエンドはそれほど簡単ではないことをバックエンド開発者に思い出させる必要があります?!!

しかし、本題に入りましょう。

I - フロントエンド構造の概要

Web フレームワークを使用したことがある場合は、Wails CLI が内部で ViteJs を使用していることがすぐにわかるでしょう。

...
.
├── index.html
├── package.json
├── package.json.md5
├── package-lock.json
├── postcss.config.js
├── README.md
├── src
│   ├── App.svelte
│   ├── assets
│   │   ├── fonts
│   │   │   ├── nunito-v16-latin-regular.woff2
│   │   │   └── OFL.txt
│   │   └── images
│   │       └── logo-universal.png
│   ├── lib
│   │   ├── BackBtn.svelte
│   │   ├── BottomActions.svelte
│   │   ├── EditActions.svelte
│   │   ├── EntriesList.svelte
│   │   ├── Language.svelte
│   │   ├── popups
│   │   │   ├── alert-icons.ts
│   │   │   └── popups.ts
│   │   ├── ShowPasswordBtn.svelte
│   │   └── TopActions.svelte
│   ├── locales
│   │   ├── en.json
│   │   └── es.json
│   ├── main.ts
│   ├── pages
│   │   ├── About.svelte
│   │   ├── AddPassword.svelte
│   │   ├── Details.svelte
│   │   ├── EditPassword.svelte
│   │   ├── Home.svelte
│   │   ├── Login.svelte
│   │   └── Settings.svelte
│   ├── style.css
│   └── vite-env.d.ts
├── svelte.config.js
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wailsjs
    ├── go
    │   ├── main
    │   │   ├── App.d.ts
    │   │   └── App.js
    │   └── models.ts
    └── runtime
        ├── package.json
        ├── runtime.d.ts
        └── runtime.js

...
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

Vite によって生成された Web フレームワークを使用したことがある場合は、その設定ファイルに驚かないでしょう。ここでは Svelte5 (さらに Taildwindcss Daisyui の構成) を使用します。これは、すでに説明したように、独自の bash スクリプトを生成するものです。また、フロントエンドの開発を容易にする TypeScript も使用しているため、その構成も確認できます。

しかし、この説明で重要なことは、wailsjs フォルダーの内容です。ここで、Wails によるコンピレーションが魔法を発揮しました。 go サブフォルダーは、フロントエンドと対話する必要があるバックエンド部分の Js/Ts に「変換」されたメソッドが保存される場所です。たとえば、main/App.js (またはその TypeScript バージョン main/App.d.ts) には、アプリ構造のエクスポートされた (パブリック) メソッドがすべて存在します。

...
.
├── index.html
├── package.json
├── package.json.md5
├── package-lock.json
├── postcss.config.js
├── README.md
├── src
│   ├── App.svelte
│   ├── assets
│   │   ├── fonts
│   │   │   ├── nunito-v16-latin-regular.woff2
│   │   │   └── OFL.txt
│   │   └── images
│   │       └── logo-universal.png
│   ├── lib
│   │   ├── BackBtn.svelte
│   │   ├── BottomActions.svelte
│   │   ├── EditActions.svelte
│   │   ├── EntriesList.svelte
│   │   ├── Language.svelte
│   │   ├── popups
│   │   │   ├── alert-icons.ts
│   │   │   └── popups.ts
│   │   ├── ShowPasswordBtn.svelte
│   │   └── TopActions.svelte
│   ├── locales
│   │   ├── en.json
│   │   └── es.json
│   ├── main.ts
│   ├── pages
│   │   ├── About.svelte
│   │   ├── AddPassword.svelte
│   │   ├── Details.svelte
│   │   ├── EditPassword.svelte
│   │   ├── Home.svelte
│   │   ├── Login.svelte
│   │   └── Settings.svelte
│   ├── style.css
│   └── vite-env.d.ts
├── svelte.config.js
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wailsjs
    ├── go
    │   ├── main
    │   │   ├── App.d.ts
    │   │   └── App.js
    │   └── models.ts
    └── runtime
        ├── package.json
        ├── runtime.d.ts
        └── runtime.js

...
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

全員が約束を返します。 Promise が戻り値の型として使用される Go 構造体を「ラップ」するか、それぞれの関数が引数の型を取る場合、Go に対応するクラスを含むモジュール (models.ts、この場合は TypeScript を使用するため型付けされます) が存在します。名前空間内の構造体とそのコンストラクター。

さらに、ランタイム サブフォルダーには Go のランタイム パッケージのすべてのメソッドが含まれており、これによりバックエンドでリッスンまたはバックエンドから送出されるウィンドウとイベントをそれぞれ操作できるようになります。

src フォルダーには、他の Web アプリケーションと同様に、Vite によってコンパイルされて「frontend/dist」に保存される (そして最終的な実行可能ファイルに埋め込まれる) ファイルが含まれています。 Tailwindcss を使用しているため、style.css には基本的な Tailwind 設定と使用する必要がある CSS クラスが含まれていることに注意してください。また、インターフェースに Web テクノロジーを使用する利点として、1 つ以上のフォント (フォルダー アセット/フォント) を簡単に使用したり、交換したりできることが挙げられます。

この概要の最後に、開発モード (wails dev) でコンパイルすると、ホットリロードができることに加えて、(バックエンドとフロントエンドの両方で) 行われた変更を観察できるだけではないことに注意してください。アプリケーション ウィンドウ自体だけでなく、Web サーバーが起動しているため、アドレス http://localhost:34115 を介して Web ブラウザでも実行できます。これにより、お気に入りのブラウザ開発拡張機能を使用できるようになります。 Wails 自身がいくつかの非常に便利な開発ツールを提供していると言わなければなりませんが、アプリケーション ウィンドウを右クリックし (開発モードのみ)、[要素の検査] を選択すると次のようになります。

A minimalist password manager desktop app: a foray into Golang

II - それでは、HTML、CSS、JavaScript について詳しく見ていきましょう。


// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {models} from '../models';

export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>;

export function CheckMasterPassword(arg1:string):Promise<boolean>;

export function DeleteEntry(arg1:string):Promise<void>;

export function Drop():Promise<void>;

export function GetAllEntries():Promise<Array<models.PasswordEntry>>;

export function GetEntryById(arg1:string):Promise<models.PasswordEntry>;

export function GetLanguage():Promise<string>;

export function GetMasterPassword():Promise<boolean>;

export function GetPasswordCount():Promise<number>;

export function SaveLanguage(arg1:string):Promise<void>;

export function SaveMasterPassword(arg1:string):Promise<string>;

export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

ご覧のとおり、Svelte に追加した JavaScript パッケージは 4 つあります (前述の Tailwindcss Daisyui とは別に):

  • svelte-copy、ユーザー名とパスワードをクリップボードに簡単にコピーできるようにします。
  • svelte-i18n、i18n 処理用、つまりユーザーがアプリケーションの言語を変更できるようにします。
  • svelte-spa-router、Svelte 用の小さなルーティング ライブラリ。これにより、アプリケーション ウィンドウでのビューの変更が簡単になります。この場合、提供される「公式」ルーティングを使用する価値がないからです。 SvelteKit.
  • sweetalert2、基本的にはモーダル/ダイアログ ボックスを簡単かつ迅速に作成するために使用します。

すべての SPA のエントリ ポイントは main.js (または main.ts) ファイルなので、そこから始めましょう:

...
.
├── index.html
├── package.json
├── package.json.md5
├── package-lock.json
├── postcss.config.js
├── README.md
├── src
│   ├── App.svelte
│   ├── assets
│   │   ├── fonts
│   │   │   ├── nunito-v16-latin-regular.woff2
│   │   │   └── OFL.txt
│   │   └── images
│   │       └── logo-universal.png
│   ├── lib
│   │   ├── BackBtn.svelte
│   │   ├── BottomActions.svelte
│   │   ├── EditActions.svelte
│   │   ├── EntriesList.svelte
│   │   ├── Language.svelte
│   │   ├── popups
│   │   │   ├── alert-icons.ts
│   │   │   └── popups.ts
│   │   ├── ShowPasswordBtn.svelte
│   │   └── TopActions.svelte
│   ├── locales
│   │   ├── en.json
│   │   └── es.json
│   ├── main.ts
│   ├── pages
│   │   ├── About.svelte
│   │   ├── AddPassword.svelte
│   │   ├── Details.svelte
│   │   ├── EditPassword.svelte
│   │   ├── Home.svelte
│   │   ├── Login.svelte
│   │   └── Settings.svelte
│   ├── style.css
│   └── vite-env.d.ts
├── svelte.config.js
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wailsjs
    ├── go
    │   ├── main
    │   │   ├── App.d.ts
    │   │   └── App.js
    │   └── models.ts
    └── runtime
        ├── package.json
        ├── runtime.d.ts
        └── runtime.js

...
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

Wails CLI によって生成されたスケルトンに追加したものを強調表示しました。 svelte-i18n ライブラリでは、フォールバック/初期 言語の設定と同時に、翻訳を含む JSON ファイルを main.js/ts ファイルに登録する必要があります (ただし、これは、ユーザーが好みとして選択した内容に基づいて後で操作されます)。翻訳を含む JSON ファイルの形式は次のとおりです:

// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {models} from '../models';

export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>;

export function CheckMasterPassword(arg1:string):Promise<boolean>;

export function DeleteEntry(arg1:string):Promise<void>;

export function Drop():Promise<void>;

export function GetAllEntries():Promise<Array<models.PasswordEntry>>;

export function GetEntryById(arg1:string):Promise<models.PasswordEntry>;

export function GetLanguage():Promise<string>;

export function GetMasterPassword():Promise<boolean>;

export function GetPasswordCount():Promise<number>;

export function SaveLanguage(arg1:string):Promise<void>;

export function SaveMasterPassword(arg1:string):Promise<string>;

export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

このライブラリのシステムは、Svelte アプリケーションの翻訳を容易にするのに簡単で便利だと思います (詳細についてはドキュメントを参照してください):

/* package.json */
...
},
  "dependencies": {
    "svelte-copy": "^2.0.0",
    "svelte-i18n": "^4.0.1",
    "svelte-spa-router": "^4.0.1",
    "sweetalert2": "^11.14.5"
  }
...

ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

このようなサイトを使用すると、JSON ファイルをさまざまな言語に翻訳できます。ただし、問題は、.svelte ファイルに $format を入力するときに手動でファイルを追跡する必要があり、面倒でエラーが発生しやすいことです。このタスクを自動化する方法がわかりません。ご存知の方がいらっしゃいましたら、教えていただければ幸いです。

他の Svelte アプリケーションと同様に、インターフェイスを構築する次のステップは App.svelte ファイルです。

/* main.ts */

import { mount } from 'svelte'
import './style.css'
import App from './App.svelte'
import { addMessages, init } from "svelte-i18n"; // ⇐ ⇐
import en from './locales/en.json'; // ⇐ ⇐
import es from './locales/es.json'; // ⇐ ⇐

addMessages('en', en); // ⇐ ⇐
addMessages('es', es); // ⇐ ⇐

init({
  fallbackLocale: 'en', // ⇐ ⇐
  initialLocale: 'en', // ⇐ ⇐
});

const app = mount(App, {
  target: document.getElementById('app')!,
})

export default app

ログイン後にコピー
ログイン後にコピー

ここでは、アプリケーションのコンパイル時に自動的に生成され、struct App のパブリック メソッドとして宣言された バインディングである GetMasterPassword を使用します (このシリーズの最初の部分を参照)。この関数はデータベースにクエリを実行し、データベースにマスター パスワードが登録されている場合は、ユーザーがすでに登録されているものとみなし (ブール値をラップする Promise を返します)、残りの部分にアクセスできるようにするためにそのパスワードを入力するよう求めます。ビューの。データベースにマスター パスワードがない場合、ユーザーは「新規」とみなされ、初めてアプリケーションに入るために独自のパスワードを生成することが求められます。

最後に、Login.svelte コンポーネントをマウントするときに、アプリケーションの残りの部分にとって重要な作業を行います。すでに見たように、svelte-i18n ライブラリでは初期言語コードの宣言が強制されますが、Login.svelte をマウントするときに、言語コードが保存されているかどうかをデータベースに (GetLanguage バインディングを使用して) 確認します。データベースが空の文字列を返した場合、つまり、ユーザーの設定として設定された言語がない場合、svelte-i18n は、initialLocale として設定された値を使用します。代わりに言語が設定されている場合は、その言語が設定され (locale.set(result);)、「change_titles」イベントが発行され、アプリのタイトル バーとネイティブ ダイアログの翻訳されたタイトルが渡されます。バックエンドが処理するもの:

/* frontend/src/locales/en.json */

{
    "language": "Language",
    "app_title": "Nu-i uita • minimalist password store",
    "select_directory": "Select the directory where to save the data export",
    "select_file": "Select the backup file to import",
    "master_password": "Master Password ?",
    "generate": "Generate",
    "insert": "Insert",
    "login": "Login",
    ...
}


/* frontend/src/locales/es.json */

{
    "language": "Idioma",
    "app_title": "Nu-i uita • almacén de contraseñas minimalista",
    "select_directory": "Selecciona el directorio donde guardar los datos exportados",
    "select_file": "Selecciona el archivo de respaldo que deseas importar",
    "master_password": "Contraseña Maestra ?",
    "generate": "Generar",
    "insert": "Insertar",
    "login": "Inciar sesión",
    ...
}

ログイン後にコピー
ログイン後にコピー

ログインを処理するロジックは次のとおりです:

...
.
├── index.html
├── package.json
├── package.json.md5
├── package-lock.json
├── postcss.config.js
├── README.md
├── src
│   ├── App.svelte
│   ├── assets
│   │   ├── fonts
│   │   │   ├── nunito-v16-latin-regular.woff2
│   │   │   └── OFL.txt
│   │   └── images
│   │       └── logo-universal.png
│   ├── lib
│   │   ├── BackBtn.svelte
│   │   ├── BottomActions.svelte
│   │   ├── EditActions.svelte
│   │   ├── EntriesList.svelte
│   │   ├── Language.svelte
│   │   ├── popups
│   │   │   ├── alert-icons.ts
│   │   │   └── popups.ts
│   │   ├── ShowPasswordBtn.svelte
│   │   └── TopActions.svelte
│   ├── locales
│   │   ├── en.json
│   │   └── es.json
│   ├── main.ts
│   ├── pages
│   │   ├── About.svelte
│   │   ├── AddPassword.svelte
│   │   ├── Details.svelte
│   │   ├── EditPassword.svelte
│   │   ├── Home.svelte
│   │   ├── Login.svelte
│   │   └── Settings.svelte
│   ├── style.css
│   └── vite-env.d.ts
├── svelte.config.js
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wailsjs
    ├── go
    │   ├── main
    │   │   ├── App.d.ts
    │   │   └── App.js
    │   └── models.ts
    └── runtime
        ├── package.json
        ├── runtime.d.ts
        └── runtime.js

...
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

簡単に言うと、ユーザーが入力した内容を取得する入力にバインドされた状態である newPassword は、最初に onLogin によってチェックされ、少なくとも 6 文字が含まれているかどうか、およびすべてが ASCII 文字であるかどうかが確認されます。つまり、この小さな関数 const isAscii = (str: string): boolean によって、それらはわずか 1 バイトの長さになります (その理由については、このシリーズのパート 1 を参照してください)。 => /^[x00-x7F] $/.test(str);。チェックが失敗した場合、関数は戻り、ユーザーに警告 トースト を表示します。その後、データベースにマスター パスワードが保存されていない場合 (isLogin = false)、ユーザーが入力した内容はすべて SaveMasterPassword 関数 (Wails によって生成されたバインディング) によって保存されます。 Promise が正常に解決された場合 (データベースに保存されているレコードの Id として uuid 文字列を返す)、ユーザーは svelte-spa-router によってホーム ビューに移動します。ライブラリのプッシュメソッド。逆に、パスワードの長さと 非 ASCII 文字の有無のチェックに合格し、DB 内にマスター パスワードがある (isLogin = true) 場合、CheckMasterPassword 関数は保存されているパスワードと照合してその ID を検証し、次のいずれかを実行します。ユーザーをホーム ビューに移動します (プロミスは true で解決されます)。または、入力されたパスワードが間違っていたことを示す トースト が表示されます。

アプリケーションの中心となるビューであると同時に、最も複雑なのはホーム ビューです。その HTML は実際には 3 つのコンポーネントに分割されています。検索入力のある上部のボタン バー (TopActions コンポーネント)、下部のボタン バー (BottomActions コンポーネント)、および保存されたパスワード エントリの総数またはそのリストが表示される中央領域です。スクロール可能なウィンドウ (EntriesList コンポーネント):

// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {models} from '../models';

export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>;

export function CheckMasterPassword(arg1:string):Promise<boolean>;

export function DeleteEntry(arg1:string):Promise<void>;

export function Drop():Promise<void>;

export function GetAllEntries():Promise<Array<models.PasswordEntry>>;

export function GetEntryById(arg1:string):Promise<models.PasswordEntry>;

export function GetLanguage():Promise<string>;

export function GetMasterPassword():Promise<boolean>;

export function GetPasswordCount():Promise<number>;

export function SaveLanguage(arg1:string):Promise<void>;

export function SaveMasterPassword(arg1:string):Promise<string>;

export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

つまり、検索状態 (searchTerms) を空の文字列にし、検索語がある場合はリセットされ、リスト全体が表示されます。一方、親コンポーネントがリストを表示または非表示にするように、showList 状態 (TopActions の props isEntriesList) を切り替えます。

上の図からわかるように、両方の子コンポーネントは親の searchTerms 状態と同じ props を共有します。 TopActions コンポーネントはユーザーからの入力を取得し、それを状態として親コンポーネント Home に渡し、次にそれを props としてその子コンポーネント EntriesList.

に渡します。

完全なリスト、またはユーザーが入力した検索語でフィルターされたリストを表示するメイン ロジックは、期待どおり、EntriesList コンポーネントによって実行されます。

...
.
├── index.html
├── package.json
├── package.json.md5
├── package-lock.json
├── postcss.config.js
├── README.md
├── src
│   ├── App.svelte
│   ├── assets
│   │   ├── fonts
│   │   │   ├── nunito-v16-latin-regular.woff2
│   │   │   └── OFL.txt
│   │   └── images
│   │       └── logo-universal.png
│   ├── lib
│   │   ├── BackBtn.svelte
│   │   ├── BottomActions.svelte
│   │   ├── EditActions.svelte
│   │   ├── EntriesList.svelte
│   │   ├── Language.svelte
│   │   ├── popups
│   │   │   ├── alert-icons.ts
│   │   │   └── popups.ts
│   │   ├── ShowPasswordBtn.svelte
│   │   └── TopActions.svelte
│   ├── locales
│   │   ├── en.json
│   │   └── es.json
│   ├── main.ts
│   ├── pages
│   │   ├── About.svelte
│   │   ├── AddPassword.svelte
│   │   ├── Details.svelte
│   │   ├── EditPassword.svelte
│   │   ├── Home.svelte
│   │   ├── Login.svelte
│   │   └── Settings.svelte
│   ├── style.css
│   └── vite-env.d.ts
├── svelte.config.js
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wailsjs
    ├── go
    │   ├── main
    │   │   ├── App.d.ts
    │   │   └── App.js
    │   └── models.ts
    └── runtime
        ├── package.json
        ├── runtime.d.ts
        └── runtime.js

...
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

前述したように、2 つのプロパティ (listCounter と search) が受信され、状態が維持されます (let エントリ: models.PasswordEntry[] = $state([]);)。ユーザーの要求に応じてコンポーネントがマウントされると、バックエンドは保存されたパスワード エントリの完全なリストを要求されます。検索語がない場合は、状態に保存されます。存在する場合、取得された配列の単純なフィルタリングが実行され、次の状態に保存されます:

// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {models} from '../models';

export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>;

export function CheckMasterPassword(arg1:string):Promise<boolean>;

export function DeleteEntry(arg1:string):Promise<void>;

export function Drop():Promise<void>;

export function GetAllEntries():Promise<Array<models.PasswordEntry>>;

export function GetEntryById(arg1:string):Promise<models.PasswordEntry>;

export function GetLanguage():Promise<string>;

export function GetMasterPassword():Promise<boolean>;

export function GetPasswordCount():Promise<number>;

export function SaveLanguage(arg1:string):Promise<void>;

export function SaveMasterPassword(arg1:string):Promise<string>;

export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

表示されたリストで、ユーザーは 2 つのアクションを実行できます。 1 つ目は、エントリの詳細を表示することで、対応するボタンをクリックすると実行されます。 Push(`/details/${entry.Id}`)}。基本的に、ルーティング ライブラリのプッシュ メソッドを呼び出してユーザーを詳細ビューに移動しますが、問題のアイテムに対応する Id パラメーターを渡します。

ユーザーが実行できるもう 1 つのアクションは、リストから項目を削除することです。対応するボタンをクリックすると、確認ポップアップが表示され、showAlert 関数が呼び出されます。この関数は、実際には sweetalert2 ライブラリの抽象化レイヤーである showWarning を呼び出します (sweetalert2 ライブラリを呼び出すすべての関数は、frontend/src/lib/popups/popups.ts にあります)。ユーザーが削除アクションを確認すると、DeleteEntry バインディングが呼び出され (DB から削除するため)、返された Promise が解決されると、deleteItem が呼び出されます (エントリ状態に格納されている配列から削除するため)。 :

/* package.json */
...
},
  "dependencies": {
    "svelte-copy": "^2.0.0",
    "svelte-i18n": "^4.0.1",
    "svelte-spa-router": "^4.0.1",
    "sweetalert2": "^11.14.5"
  }
...

ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

ホーム ビューの他のコンポーネント (BottomActions) は非常に単純です。props を受け取らず、ユーザーをさまざまなビュー (設定、バージョン情報、または追加パスワード) にリダイレクトすることに限定されています。

AddPassword ビューと EditPassword ビューは非常によく似たロジックを共有し、Login ビューにも似ています。どちらの場合も、ユーザーはテキスト入力で入力した内容の先頭と末尾にスペースを入力することはできません。また、パスワードの長さは少なくとも 6 文字の ASCII 文字である必要があるというログイン ビューと同じポリシーに従います。基本的に、それらを区別するのは、実行する必要があるアクションに関連する Wails が生成したリンクを呼び出すことです。

/* main.ts */

import { mount } from 'svelte'
import './style.css'
import App from './App.svelte'
import { addMessages, init } from "svelte-i18n"; // ⇐ ⇐
import en from './locales/en.json'; // ⇐ ⇐
import es from './locales/es.json'; // ⇐ ⇐

addMessages('en', en); // ⇐ ⇐
addMessages('es', es); // ⇐ ⇐

init({
  fallbackLocale: 'en', // ⇐ ⇐
  initialLocale: 'en', // ⇐ ⇐
});

const app = mount(App, {
  target: document.getElementById('app')!,
})

export default app

ログイン後にコピー
ログイン後にコピー

やや複雑なもう 1 つのビューは、[設定] です。これには、親コンポーネント (設定) から props 言語名を受け取る言語コンポーネントがあります:

/* frontend/src/locales/en.json */

{
    "language": "Language",
    "app_title": "Nu-i uita • minimalist password store",
    "select_directory": "Select the directory where to save the data export",
    "select_file": "Select the backup file to import",
    "master_password": "Master Password ?",
    "generate": "Generate",
    "insert": "Insert",
    "login": "Login",
    ...
}


/* frontend/src/locales/es.json */

{
    "language": "Idioma",
    "app_title": "Nu-i uita • almacén de contraseñas minimalista",
    "select_directory": "Selecciona el directorio donde guardar los datos exportados",
    "select_file": "Selecciona el archivo de respaldo que deseas importar",
    "master_password": "Contraseña Maestra ?",
    "generate": "Generar",
    "insert": "Insertar",
    "login": "Inciar sesión",
    ...
}

ログイン後にコピー
ログイン後にコピー

このコンポーネントの HTML は、ユーザーの言語選択を処理する単一の select です。 onchange イベントで、次の 3 つのことを行う関数 (handleChange) を受け取ります。

  • svelte-i18n ライブラリを使用してフロントエンドの言語を設定します
  • イベント ("change_titles") を発行し、Wails ランタイムがアプリケーションのタイトル バーのタイトルと、関連する ディレクトリの選択 および ファイルの選択 ダイアログ ボックスのタイトルを変更するようにします。前のアクションへ
  • ユーザーが選択した言語を DB に保存し、次回アプリケーションを起動したときにその言語で構成された状態で開きます。

[設定] ビューに戻ると、その操作全体がバックエンドとの間で送受信される一連のイベントによって制御されます。最も単純なのは [終了] ボタンです。ユーザーがボタンをクリックすると、終了イベントがトリガーされてバックエンドでリッスンされ、アプリケーションが終了します (onclick={() => EventsEmit("quit")})。 ヒントは、すでに説明したように、Escape キー (ショートカット) が同じアクションを実行することをユーザーに通知します。

リセット ボタンは、ポップアップ ウィンドウを表示する関数を呼び出します。

...
.
├── index.html
├── package.json
├── package.json.md5
├── package-lock.json
├── postcss.config.js
├── README.md
├── src
│   ├── App.svelte
│   ├── assets
│   │   ├── fonts
│   │   │   ├── nunito-v16-latin-regular.woff2
│   │   │   └── OFL.txt
│   │   └── images
│   │       └── logo-universal.png
│   ├── lib
│   │   ├── BackBtn.svelte
│   │   ├── BottomActions.svelte
│   │   ├── EditActions.svelte
│   │   ├── EntriesList.svelte
│   │   ├── Language.svelte
│   │   ├── popups
│   │   │   ├── alert-icons.ts
│   │   │   └── popups.ts
│   │   ├── ShowPasswordBtn.svelte
│   │   └── TopActions.svelte
│   ├── locales
│   │   ├── en.json
│   │   └── es.json
│   ├── main.ts
│   ├── pages
│   │   ├── About.svelte
│   │   ├── AddPassword.svelte
│   │   ├── Details.svelte
│   │   ├── EditPassword.svelte
│   │   ├── Home.svelte
│   │   ├── Login.svelte
│   │   └── Settings.svelte
│   ├── style.css
│   └── vite-env.d.ts
├── svelte.config.js
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wailsjs
    ├── go
    │   ├── main
    │   │   ├── App.d.ts
    │   │   └── App.js
    │   └── models.ts
    └── runtime
        ├── package.json
        ├── runtime.d.ts
        └── runtime.js

...
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

ユーザーがアクションを受け入れると、Drop バインディングが呼び出され、DB 内のすべての コレクション がクリーンアップされ、返された Promise が解決されると、ユーザーがログイン ビューに送信され、次のことが表示されます。アクションの成功を示すモーダル。

残りの 2 つのアクションは互いに似ているので、データのインポートを見てみましょう。

ユーザーが対応するボタンをクリックすると、イベントが発行され (onclick={() => EventsEmit("import_data")})、バックエンドでリッスンされます。受信すると、ネイティブの ファイルの選択 ダイアログ ボックスが開き、ユーザーがバックアップ ファイルを選択できるようになります。ユーザーがファイルを選択すると、パス (fileLocation) を含む変数には空の文字列が含まれないため、バックエンドでイベント (「enter_password」) がトリガーされ、フロントエンドでこのイベントがリッスンされ、次に表示されます。エクスポート時に使用されたマスターパスワードを尋ねる新しいポップアップウィンドウが表示されます。ここでも、フロントエンドは、ユーザーが入力したマスター パスワードを運ぶ別のイベント (「パスワード」) を発行します。この新しいイベントは、バックエンドで受信されると、DB パッケージの ImportDump メソッドを実行します。このメソッドは、ユーザーが選択したバックアップ ファイルから DB 内のデータを読み取り、復元する作業を実行します。その結果、新しいイベント (「imported_data」) が発行され、実行の結果 (成功または失敗) が添付データとして送信されます。フロントエンドはイベントを受信すると、次の 2 つのタスクのみを実行する必要があります:

  • 結果が成功した場合は、バックアップ ファイルに保存された言語を設定し、アクションの成功を示すモーダルを表示します
  • 何らかの理由でインポートを実行できなかった場合は、エラーとその原因を表示します。

これらはすべて、言葉で説明するよりもコード ロジックで確認する方がはるかに簡単です ?:

...
.
├── index.html
├── package.json
├── package.json.md5
├── package-lock.json
├── postcss.config.js
├── README.md
├── src
│   ├── App.svelte
│   ├── assets
│   │   ├── fonts
│   │   │   ├── nunito-v16-latin-regular.woff2
│   │   │   └── OFL.txt
│   │   └── images
│   │       └── logo-universal.png
│   ├── lib
│   │   ├── BackBtn.svelte
│   │   ├── BottomActions.svelte
│   │   ├── EditActions.svelte
│   │   ├── EntriesList.svelte
│   │   ├── Language.svelte
│   │   ├── popups
│   │   │   ├── alert-icons.ts
│   │   │   └── popups.ts
│   │   ├── ShowPasswordBtn.svelte
│   │   └── TopActions.svelte
│   ├── locales
│   │   ├── en.json
│   │   └── es.json
│   ├── main.ts
│   ├── pages
│   │   ├── About.svelte
│   │   ├── AddPassword.svelte
│   │   ├── Details.svelte
│   │   ├── EditPassword.svelte
│   │   ├── Home.svelte
│   │   ├── Login.svelte
│   │   └── Settings.svelte
│   ├── style.css
│   └── vite-env.d.ts
├── svelte.config.js
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── wailsjs
    ├── go
    │   ├── main
    │   │   ├── App.d.ts
    │   │   └── App.js
    │   └── models.ts
    └── runtime
        ├── package.json
        ├── runtime.d.ts
        └── runtime.js

...
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

フロントエンド (EventsOn) にリスナーを登録する Wails ランタイム関数は関数を返し、呼び出されたときにそのリスナーをキャンセルすることに言及する価値があります。コンポーネントが破棄されたときに、前述のリスナーをキャンセルすると便利です。 React と同様に、onMount フックは、リスナーにクリーンアップ関数を返させることで、リスナーを「クリーンアップ」できます。この場合、別個に保存するように予防策を講じた EventsOn によって返されるすべての関数を呼び出します。変数:

// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {models} from '../models';

export function AddPasswordEntry(arg1:string,arg2:string,arg3:string):Promise<string>;

export function CheckMasterPassword(arg1:string):Promise<boolean>;

export function DeleteEntry(arg1:string):Promise<void>;

export function Drop():Promise<void>;

export function GetAllEntries():Promise<Array<models.PasswordEntry>>;

export function GetEntryById(arg1:string):Promise<models.PasswordEntry>;

export function GetLanguage():Promise<string>;

export function GetMasterPassword():Promise<boolean>;

export function GetPasswordCount():Promise<number>;

export function SaveLanguage(arg1:string):Promise<void>;

export function SaveMasterPassword(arg1:string):Promise<string>;

export function UpdateEntry(arg1:models.PasswordEntry):Promise<boolean>;
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

アプリケーションのフロントエンド部分のレビューを終えるには、About コンポーネントについて少し説明するだけです。通常のアバウト表示のように、アプリケーションに関する情報の表示に限定されているため、これにはほとんどロジックがありません。ただし、ご覧のとおり、ビューにはアプリケーション リポジトリへのリンクが表示されていることに注意してください。明らかに、通常の Web ページでは、アンカー タグ () によって対応するリンクに移動しますが、デスクトップ アプリケーションでは、Wails の実行時にこのための特定の関数 (BrowserOpenURL) がなければ、これは起こりません。 :

/* package.json */
...
},
  "dependencies": {
    "svelte-copy": "^2.0.0",
    "svelte-i18n": "^4.0.1",
    "svelte-spa-router": "^4.0.1",
    "sweetalert2": "^11.14.5"
  }
...

ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

これにより、バイナリが build/bin フォルダーにビルドされます。ただし、他のビルド オプションを選択したり、クロスコンパイルを実行したりするには、Wails CLI のドキュメントを参照することをお勧めします。

このアプリケーションについては、このシリーズの最初の部分ですでに述べたと思いますが、Windows と Linux のコンパイルにのみ焦点を当ててきました。これらのタスク (テストのため反復的です) を快適な方法で実行するために、いくつかの小さなスクリプトと、それらを「調整」する Makefile を作成しました。

make create-bundles コマンドは、Linux バージョンの場合、アプリケーションを含む .tar.xz 圧縮ファイルと、実行可能ファイルをインストールする「インストーラー」として機能する Makefile、スタート メニュー と対応するアプリケーション アイコン。 Windows バージョンの場合、バイナリは dist/ というフォルダー内の .zip として単純に圧縮されます。ただし、クロスプラットフォームの自動ビルドを希望する場合、Wails には、生成されたアーティファクトをアップロード (デフォルト オプション) できる Github Actions があります。リポジトリに追加します。

実行時に make create-bundles コマンドを使用すると、Wails コマンド wails build -clean -upx (Linux の場合) または wails build -skipbindings -s -platform windows/amd64 - が呼び出されることに注意してください。 upx (Windows の場合)。 -upx フラグは、コンピューターにインストールされている

UPX ユーティリティを使用したバイナリの圧縮を指します。実行可能ファイルのサイズが小さい秘密の一部は、このユーティリティが実行する素晴らしい圧縮ジョブによるものです。

最後に、ビルド スクリプトによって現在のリポジトリ タグが About ビューに自動的に追加され、ビルド後にその値がデフォルト (DEV_VERSION) に復元されることに注意してください。

ふぅ!この2記事は思ったより長くなってしまいました!しかし、これらを気に入っていただければ幸いです。そして何よりも、新しいプロジェクトを考えるのに役立つことを願っています。プログラミングで何かを学ぶことは、このように機能します…

すべてのアプリケーション コードはこの GitHub リポジトリにあることに注意してください。

また他の投稿でお会いしましょう。コーディングを楽しんでください?!!

以上がシンプルなパスワード マネージャー デスクトップ アプリ: Golang の Wails フレームワークへの進出 (パート 2)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート