本資料では、gkillの主要なプログラム構成要素と処理フローについて説明します。バックエンド(Go)とフロントエンド(Vue 3 + TypeScript)の両面をカバーします。
gkillには2つのエントリーポイントがあります。
| バイナリ | ソース | 特徴 |
|---|---|---|
gkill_server |
gkill/main/gkill_server/main.go |
ヘッドレスHTTPサーバー |
gkill |
gkill/main/gkill/main.go |
デスクトップアプリ(go-astilectron) |
両者ともgkill/main/common/パッケージの共通初期化ロジックを使用します。
sequenceDiagram
participant Main as main()
participant Common as common パッケージ
participant Options as gkill_options
participant Log as gkill_log
participant API as GkillServerAPI
participant DAO as GkillDAOManager
Main->>Common: init()
Note over Common: HOME環境変数の設定<br/>タイムゾーン修正(Android対応)
Main->>Common: InitGkillOptions()
Common->>Options: 各ディレクトリパス設定
Note over Options: LibDir, CacheDir, LogDir,<br/>ConfigDir, TLSファイルパス,<br/>DataDirectoryDefault
Main->>Log: gkill_log.Init()
Note over Log: slogハンドラ設定<br/>レベル別ファイル出力
Main->>Common: InitGkillServerAPI()
Common->>API: api.NewGkillServerAPI()
API->>DAO: dao.NewGkillDAOManager()
Note over DAO: ConfigDAOs初期化<br/>(8つの設定DAO)
Main->>Common: LaunchGkillServerAPI()
Common->>API: Serve(ctx)
Note over API: HTTPサーバー起動<br/>再起動ループ対応
gkill_optionsパッケージで定義されるディレクトリ構造を$GKILL_HOME配下に初期化します。
func InitGkillOptions() {
os.Setenv("GKILL_HOME", filepath.Clean(os.ExpandEnv(gkill_options.GkillHomeDir)))
gkill_options.LibDir = "$GKILL_HOME/lib/base_directory"
gkill_options.CacheDir = "$GKILL_HOME/caches"
gkill_options.LogDir = "$GKILL_HOME/logs"
gkill_options.ConfigDir = "$GKILL_HOME/configs"
gkill_options.TLSCertFileDefault = "$GKILL_HOME/tls/cert.cer"
gkill_options.TLSKeyFileDefault = "$GKILL_HOME/tls/key.pem"
gkill_options.DataDirectoryDefault = "$GKILL_HOME/datas"
}LaunchGkillServerAPIはサーバー設定変更時の再起動をサポートしています。
func LaunchGkillServerAPI(ctx context.Context) error {
for {
err = gkillServerAPI.Serve(ctx)
if errors.Is(err, http.ErrServerClosed) {
if ctx.Err() != nil {
return nil // SIGINT/SIGTERM → 終了
}
// HandleUpdateServerConfigs → リスタート(ループ継続)
}
err = InitGkillServerAPI() // 新しいインスタンスで再初期化
}
}type GkillDAOManager struct {
initializingMutex map[string]map[string]*sync.RWMutex // ユーザー×デバイス別初期化ロック
gkillRepositories map[string]map[string]*reps.GkillRepositories // ユーザー×デバイス別リポジトリ
gkillNotificators map[string]map[string]*GkillNotificator // 通知マネージャ
fileRepWatchCacheUpdater rep_cache_updater.FileRepCacheUpdater // ファイル監視キャッシュ更新
ConfigDAOs *ConfigDAOs // 設定データベース群
router *mux.Router // HTTPルーター
IDFIgnore []string // IDF無視パターン
skipUpdateCache *bool // キャッシュ更新スキップフラグ
}graph TD
subgraph ConfigDAOs
SC[ServerConfigDAO<br/>server_config.db]
AC[AccountDAO<br/>account.db]
LS[LoginSessionDAO<br/>account_state.db]
FU[FileUploadHistoryDAO<br/>account_state.db]
SK[ShareKyouInfoDAO<br/>share_kyou_info.db]
AP[ApplicationConfigDAO<br/>user_config.db]
RP[RepositoryDAO<br/>user_config.db]
NT[GkillNotificationTargetDAO<br/>gkill_notification_target.db]
end
subgraph "SQLite3ファイル"
F1[(server_config.db)]
F2[(account.db)]
F3[(account_state.db)]
F4[(share_kyou_info.db)]
F5[(user_config.db)]
F6[(gkill_notification_target.db)]
end
SC --> F1
AC --> F2
LS --> F3
FU --> F3
SK --> F4
AP --> F5
RP --> F5
NT --> F6
| DAO | DBファイル | 管理対象 |
|---|---|---|
| ServerConfigDAO | server_config.db | サーバーアドレス、TLS設定、デバイス名 |
| AccountDAO | account.db | ユーザーアカウント、パスワードハッシュ |
| LoginSessionDAO | account_state.db | ログインセッション(30日有効) |
| FileUploadHistoryDAO | account_state.db | ファイルアップロード履歴 |
| ShareKyouInfoDAO | share_kyou_info.db | 記録の共有設定 |
| ApplicationConfigDAO | user_config.db | アプリケーション設定 |
| RepositoryDAO | user_config.db | リポジトリ定義(RepType、パス等) |
| GkillNotificationTargetDAO | gkill_notification_target.db | プッシュ通知ターゲット |
ユーザー×デバイスの組み合わせごとに生成されるデータリポジトリの集合です。
graph LR
subgraph GkillRepositories
K[KmemoReps]
KC[KCReps]
U[URLogReps]
T[TimeIsReps]
M[MiReps]
N[NlogReps]
L[LantanaReps]
TG[TagReps]
TX[TextReps]
NF[NotificationReps]
RK[ReKyouReps]
ID[IDFKyouReps]
GP[GPSLogReps]
GC[GitCommitLogReps]
end
subgraph "書き込み先(各1つ)"
WK[WriteKmemoRep]
WKC[WriteKCRep]
WU[WriteURLogRep]
WT[WriteTimeIsRep]
WM[WriteMiRep]
WN[WriteNlogRep]
WL[WriteLantanaRep]
WTG[WriteTagRep]
WTX[WriteTextRep]
WNF[WriteNotificationRep]
WRK[WriteReKyouRep]
WID[WriteIDFKyouRep]
WGP[WriteGPSLogRep]
end
各RepTypeに対して、読み取り用は複数のリポジトリを保持可能ですが、書き込み先(Write*Rep)は1つだけです。同じRepTypeで複数のUseToWrite=trueがあるとエラーになります。
gkill/api/gkill_server_api/パッケージ(83ファイル、1ハンドラ1ファイル)がAPIの中心です。旧gkill/api/gkill_server_api.go(約14,000行)から分割・移動されました。
- HTTPサーバーの起動・停止(
serve.go,close.go) - 全84 POSTエンドポイントのハンドリング(
handle_*.go) - GkillDAOManagerの保持・提供
- 認証ミドルウェアによるセッション検証(
auth_middleware.go) - レスポンス構築
- アクセスログミドルウェア(gorilla/mux
Use()で全ルートに適用。リモートIP・メソッド・パス・ステータス・所要時間・ユーザIDをACCESSレベルで記録。実装:gkill_server_api_access_log.go)
ハンドラ登録時に3つのラッパー関数を使い分けて認証レベルを指定します。
| ラッパー関数 | 認証レベル | AuthContextの内容 | 用途 |
|---|---|---|---|
wrapNoAuth |
なし | — | login, get_shared_kyous 等 |
wrapAuth |
セッション認証 | Account | logout, reset_password 等 |
wrapAuthRepos |
セッション+リポジトリ | Account, Device, GkillRepositories | データCRUD系ハンドラ |
auth_context.goで定義。認証ミドルウェアが抽出した情報を格納し、各ハンドラに渡します。
type AuthContext struct {
Account *reps.Account
Device *reps.Device
GkillRepositories *reps.GkillRepositories
}authMiddleware: リクエストからsession_idを取得 → LoginSessionDAOでセッション検証 → AccountDAO からアカウント取得 →AuthContext.Accountに設定authWithReposMiddleware: 上記に加え → DeviceDAO からデバイス取得 → GkillDAOManager.GetRepositories() →AuthContext.Device,AuthContext.GkillRepositoriesに設定
gkill/usecase/パッケージ(16ファイル)は、ハンドラから抽出されたHTTP非依存のビジネスロジックを提供します。
- DAO/リポジトリ型を直接操作する関数群
- HTTPリクエスト/レスポンスに依存しない
- ハンドラとMCPサーバーの両方から再利用可能
| カテゴリ | エンドポイント数 | 例 |
|---|---|---|
| 認証 | 6 | login, logout, reset_password, set_new_password, add_user, update_account_status |
| データ取得 | 20 | get_kyous, get_kyou, get_kmemo, get_timeis, get_mi, get_mi_board_list, get_all_tag_names, get_tags_by_id, get_tag_histories_by_tag_id 等 |
| データ追加 | 11 | add_kmemo, add_timeis, add_mi, add_lantana, add_kc, add_nlog, add_urlog, add_tag, add_text, add_gkill_notification, add_rekyou |
| データ更新 | 13 | update_kmemo, update_timeis, update_mi, update_lantana, update_kc, update_nlog, update_urlog, update_tag, update_text, update_gkill_notification, update_rekyou, update_idf_kyou, update_application_config |
| 共有 | 5 | get_share_kyou_list_infos, add_share_kyou_list_info, update_share_kyou_list_info, delete_share_kyou_list_infos, get_shared_kyous |
| 通知 | 2 | get_gkill_notification_public_key, register_gkill_notification |
| 設定 | 5 | get_server_configs, update_server_configs, get_repositories, update_user_reps, reload_repositories |
| KFTL | 2 | submit_kftl_text, get_kftl_template |
| トランザクション | 2 | commit_tx, discard_tx |
| キャッシュ | 1 | update_cache |
| ファイル | 4 | upload_files, upload_gpslog_files, get_gps_log, browse_zip_contents |
| プラグイン | 4 | get_plugin_list, get_plugin_content_html, get_plugin_config_html, post_plugin_config |
| その他 | 9 | get_application_config, generate_tls_file, get_gkill_info, open_directory, open_file, urlog_bookmarklet, get_updated_datas_by_time, get_kyous_mcp, get_gkill_notifications_by_id |
gkill/api/gkill_server_api/gkill_server_api_address.goで全エンドポイントのルートが定義されます。すべてPOST /api/{endpoint}形式です。各ルートはwrapNoAuth/wrapAuth/wrapAuthReposでラップされたハンドラに紐づけられます。
すべてのAPIレスポンスは以下の共通構造を持ちます。
type Response struct {
Messages []GkillMessage `json:"messages"`
Errors []GkillError `json:"errors"`
// + エンドポイント固有のフィールド
}
type GkillError struct {
ErrorCode string `json:"error_code"`
ErrorMessage string `json:"error_message"`
}- HTTP 200: 正常応答(
errors配列で業務エラーを返す) - HTTP 403: アクセス拒否
- HTTP 500: 予期しないサーバーエラー
関連資料: クラス階層の詳細は class-diagrams.md(セクション2)、RepTypeごとの実装選択ロジックは dvnf-rep-type-spec.md(セクション5)を参照。
各データ型のリポジトリは、以下の4層で実装されています。
graph TD
subgraph "層1: インターフェース"
IF["*Repository<br/>(例: KmemoRepository)"]
end
subgraph "層2: SQLite3実装"
S3["*RepositorySQLite3Impl<br/>直接DB操作"]
end
subgraph "層3: キャッシュ付き実装"
CA["*RepositoryCachedSQLite3Impl<br/>複数リポジトリをインメモリ統合"]
end
subgraph "層4: テンポラリ実装"
TM["*RepositoryTempSQLite3Impl<br/>一時的なトランザクション用"]
end
IF --> S3
IF --> CA
IF --> TM
CA --> S3
加えて、ローカルストレージキャッシュ版(*SQLite3ImplLocalCached)があります。
graph TD
A{UseToWrite?} -->|Yes| B[SQLite3Impl<br/>通常実装]
A -->|No| C{CacheRepsLocalStorage?}
C -->|Yes| D[SQLite3ImplLocalCached<br/>ローカルキャッシュ]
C -->|No| B
B --> E{CacheInMemory?}
D --> E
E -->|Yes| F[CachedSQLite3Impl<br/>でラップ]
E -->|No| G[そのまま使用]
KFTL(gkill独自のテキスト形式)は、テキストベースで複数種類の記録を一括入力するための書式です。
gkill/api/kftl/kftl_factory.goが中心です。
type kftlFactory struct {
// KFTLテキストからリクエストを生成するファクトリ
}- KFTLテキストを受け取る
- テキストをステートメントに分割(スプリッタ定数で区切り)
- 各ステートメントを型判定
- 型に応じたリクエスト(Add/Update)を生成
- リクエストを実行
KFTLは以下のステートメント型をサポートしています。
| カテゴリ | ステートメント型 | 説明 |
|---|---|---|
| メモ | kmemo | テキストメモの追加 |
| 打刻 | timeis_start, timeis_end | 打刻の開始/終了 |
| タスク | mi | タスクの追加 |
| 数値 | kc | 数値記録 |
| 気分 | lantana | 気分値の記録 |
| 支出 | nlog | 支出記録 |
| ブックマーク | urlog | URL記録 |
| タグ | tag | タグ付け |
| テキスト | text | テキスト付与 |
| 通知 | notification | 通知設定 |
| 制御 | template, time_set | テンプレート展開、時刻設定 |
サーバ側パーサ(kftl_factory.go)は、日本語プレフィックスと ASCII プレフィックスの両方を常に受理する。例: タグは 。tag でも #tag でも受理される。セーブ文字は ! でも ! でも受理される。これにより非日本語ロケールのユーザも問題なく KFTL を使用できる。
Mi の時間フィールド(limitTime, estimateStartTime, estimateEndTime)でも、全角 ? と ASCII ? の両方を関連時刻プレフィックスとして受理する。
KFTLパーサーはコンテキストから現在時刻を計算します。
nowFromCtx(ctx) = ctx.BaseTime + AddSecond * second
テンプレート内の時刻指定により、記録時刻をオフセットできます。
| キャッシュ種別 | フラグ | 説明 |
|---|---|---|
| インメモリ | --cache_in_memory (デフォルトtrue) |
全リポジトリデータをメモリ上のSQLite3に集約 |
| ローカルストレージ | --cache_reps_local |
ローカルファイルにキャッシュコピーを保持 |
| LatestDataRepositoryAddress | — | 各データのIDから最新リポジトリアドレスを引くキャッシュ |
graph LR
subgraph "複数のSQLite3ファイル"
R1[(rep1.db)]
R2[(rep2.db)]
R3[(rep3.db)]
end
subgraph "CachedSQLite3Impl"
MC[(メモリDB<br/>:memory:)]
end
R1 --> MC
R2 --> MC
R3 --> MC
MC --> Q[検索クエリ]
複数のリポジトリファイルのデータを1つのインメモリSQLite3データベースに集約し、検索パフォーマンスを向上させます。
| パラメータ | デフォルト | 説明 |
|---|---|---|
--cache_clear_count_limit |
3000 | キャッシュアイテム数上限 |
--cache_update_duration |
1m | キャッシュ自動更新間隔 |
FileRepCacheUpdaterが対象リポジトリファイルの変更をfsnotifyで監視し、変更を検出するとキャッシュを自動更新します。
sequenceDiagram
participant Client
participant API as GkillServerAPI
participant DAO as AccountDAO
participant Session as LoginSessionDAO
Client->>API: POST /api/login<br/>{user_id, password_sha256}
API->>DAO: パスワードハッシュ照合
DAO-->>API: OK
API->>Session: セッション作成(30日有効)
Session-->>API: session_id
API-->>Client: {session_id}
Note over Client,API: 以降のリクエストにはsession_idを付与
Client->>API: POST /api/get_kyous<br/>{session_id, ...}
API->>Session: セッション検証
Session-->>API: OK(有効期限内)
API-->>Client: レスポンス
| 項目 | 値 |
|---|---|
| パスワードハッシュ | SHA256 |
| セッション有効期限 | 30日 |
| 初期ユーザー | admin(パスワードなし) |
| セッションストレージ | account_state.db(SQLite3) |
| 技術 | バージョン | 用途 |
|---|---|---|
| Vue 3 | ^3.5.35 | UIフレームワーク |
| Vuetify 4 | ^4.1.1 | UIコンポーネントライブラリ |
| Vue Router 5 | ^5.1.0 | ルーティング |
| vue-i18n 11 | ^11.4.5 | 国際化(7言語) |
| Vite 8 | ^8.0.16 | ビルドツール |
| TypeScript 6 | ~6.0.3 | 型安全性 |
| vite-plugin-pwa | ^1.3.0 | PWA対応 |
全13ルートの定義一覧は screen-transition.md を、各画面の項目定義は screen-specs.md を参照。
主要ルート: /(ログイン), /kftl(KFTL入力), /mi(タスクボード), /rykv(履歴閲覧), /kyou(記録詳細), /mkfl(打刻メモ帳), /plaing(打刻一覧), /dashboard(日次サマリー), /saihate(記録特化), /set_new_password(パスワード変更), /regist_first_account(初回登録), /shared_page(共有), /shared_mi(共有タスク)
src/client/classes/api/gkill-api.ts(約3,400行)は、バックエンドAPIとの通信を一元管理するシングルトンクラスです。
- 全84エンドポイントへのHTTPリクエスト送信
- セッションIDの管理
- リクエスト/レスポンスの型変換
- エラーハンドリング
gkillはPiniaやVuexを使用せず、Props/Emitパターンのみで状態を管理しています。
- 親→子: Propsでデータ渡し
- 子→親: Emitでイベント通知
- API通信:
GkillAPIシングルトン経由
| 種別 | 数 | 配置 |
|---|---|---|
| ページ | 14 | pages/*.vue |
| ビュー | 177 | pages/views/*.vue |
| ダイアログ | 95 | pages/dialogs/*.vue |
Vuetifyで2つのテーマを定義しています。
| テーマ名 | 種類 |
|---|---|
gkill_theme |
ライトテーマ |
gkill_dark_theme |
ダークテーマ |
vite-plugin-pwaとWorkboxによるプレキャッシュ- POSTリクエストのオフラインキャッシュ
- プッシュ通知(VAPID)
- Web Share Target(他アプリからの共有受け取り)
cobraライブラリで定義されているCLIサブコマンド一覧です。
| コマンド | 説明 |
|---|---|
version |
バージョン情報表示(バージョン、ビルド日時、コミットハッシュ) |
idf |
指定ディレクトリのIDF(IDファイル)生成 |
dvnf |
DVNFファイル操作(get/copy/move) |
generate_thumb_cache |
サムネイルキャッシュ生成 |
generate_video_cache |
動画キャッシュ生成 |
optimize |
リポジトリ最適化 |
update_cache |
キャッシュ更新(稼働中サーバーにHTTPリクエスト) |
pluginRepositoryImpl (src/server/gkill/dao/reps/plugin_repository_impl.go) がプラグインバイナリをサブプロセスとして管理する。
| 設計判断 | 内容 |
|---|---|
| Mutex の位置 | pluginRepositoryImpl struct に sync.Mutex を保持。pluginProcess struct には置かない |
| ロック範囲 | callCommand() が mu.Lock() → ensureStarted() → sendRequest() → mu.Unlock() を直列化 |
| プロセス起動 | exec.CommandContext(context.Background(), ...) — HTTP リクエストキャンセルによる強制終了を防ぐ |
| クラッシュ復旧 | sendRequest() 失敗時に started=false → ensureStarted() → sendRequest() を1回リトライ |
大きな HTML レスポンス(会話コンテンツ等)に対応するため 32MB バッファを設定:
scanner.Buffer(make([]byte, 32*1024*1024), 32*1024*1024)詳細は plugin-system.md を参照。
- folder-structure.md — ディレクトリ構成
- dvnf-rep-type-spec.md — DVNF/RepType仕様
- class-diagrams.md — クラス階層図
- sequence-diagrams.md — シーケンス図
- api-endpoints.md — APIエンドポイント一覧
- frontend-architecture.md — フロントエンド設計ガイド
- plugin-system.md — プラグインシステム仕様