1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GROWI - Ubuntu 24.04 への導入と Windows C# クライアント

Last updated at Posted at 2025-03-30

はじめに

会社勤めをしていた時、GROWI オンプレミス利用で、部署内 Wiki 運用をしていました。
GROWI を選択した理由は、グループを用いたアクセス制御ができることと、多階層で情報が管理できることでした。

個人としても GROWI を備忘録として重宝しています。

運用の一環で、GROWI API を利用して、ページ一覧(作成日・最終更新日・最終更新者・閲覧人数・Lile人数)取得を定期的に実施していました。
GROWI は 3.6.6 で運用開始して、4.5.2 にアップグレードはしましたが、下記作業などで結構手間がかかったので、それ以降、アップグレードは先延ばしとしてしまいました。

  • mongodb を 3.6→4.0→4.2→4.4 の順でアップデート
  • Bootstrap 3→4 アップデートにともなう、panel / well の card への書き換えなど

アップグレードの情報は、GROWI DOCS - アップグレード に記載されています。
現在利用のバージョンから、最新バージョンまでの各バージョンで必要となる作業を確認することができます。

現在の最新版 7.2.0 は、4.5.2 からかなり多くの機能強化がされて、重たくなった気がしますが、機能比較される WEBコンテンツの宿命ですかね。

今回は、下記について記載したいと思います。

  • Ubuntu 24.04 に GROWI 7.2.0 をインストール
  • Windows C# で、ページ一覧(作成日・最終更新日・最終更新者・閲覧人数・Lile人数)取得

2025/04/02 追記
GROWI - データのみのバックアップ/リストア という記事も投稿しています。

テスト環境

ここに記載した情報/ソースコードは、Visual Studio Community 2022 を利用した下記プロジェクトで生成したモジュールを Windows 11 24H2 で動作確認しています。

  • Windows Forms - .NET Framework 4.8
  • Windows Forms - .NET 8
  • WPF - .NET Framework 4.8
  • WPF - .NET 8

Visual Studio 2022 - .NET Framework 4.8 は、C# 7.3 が既定です。
このため、サンプルコードは、C# 7.3 機能範囲で記述しています。

GROWI は、Ubuntu 24.04 に導入します。

Ubuntu 24.04 への導入

docker, docker compose

【簡単な4つの方法】UbuntuにDockerをインストールするには |Kinsta®

まずは、docker, docker compose 環境を構築します。

$ sudo apt update
$ sudo apt install ca-certificates curl gnupg lsb-release
$ sudo mkdir -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

$ sudo chmod a+r /etc/apt/keyrings/docker.gpg
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin

docker compose 操作を sudo 不要とするために、対象ユーザに docker グループを付与します。(初期インストール時は sudo が必要)

$ sudo usermod -aG docker <ユーザ>

GROWI

インストール作業前に、growi の docker-compose.yml に対する修正を記載します。
docker-compose.yml に対するミニマムな変更は、外部からのアクセスを可能とする下記修正です。

docker-compose.yml
  ports:
    # 127.0.0.1:3000:3000    # --- Changed --- #
    - 3000:3000              # --- Changed --- #

以前、オンプレミス利用時に設定した、下記内容も設定することとします。

  • PASSWORD_SEED に値を指定(他マシンからの移行時には同一値を設定)
  • FILE_UPLOAD を local 指定
  • volumes: にホストのディレクトリを設定。docker コンテナ内にデータを隠蔽せず、ホストでアクセス可能なディレクトリにマウント。Wiki 運用開始時は GROWI としてデータアーカイブがサポートされていなかったので、この設定をすることで、データのみのバックアップが容易となりました
docker-compose.yml
  environment:
    - MONGO_URI=mongodb://mongo:27017/growi
    - ELASTICSEARCH_URI=http://elasticsearch:9200/growi
    - PASSWORD_SEED=HogePiyoFuga  # --- Changed --- #
    - FILE_UPLOAD=local           # --- Changed --- #
    # - PASSWORD_SEED=changeme    # --- Changed --- #
    # - FILE_UPLOAD=mongodb   # activate this line if you use MongoDB GridFS rather than AWS
    # - FILE_UPLOAD=local     # activate this line if you use local storage of server rather than AWS
docker-compose.yml
# volumes:
#  growi_data: 
#  mongo_configdb:
#  mongo_db:
#  es_data:
# --- Setting Changed --- #
volumes:
  growi_data:
    driver_opts:
      type: none
      device: /opt/growi/data/growi_data
      o: bind
  mongo_configdb:
    driver_opts:
      type: none
      device: /opt/growi/data/mongo_configdb
      o: bind
  mongo_db:
    driver_opts:
      type: none
      device: /opt/growi/data/mongo_db
      o: bind
  es_data:
    driver_opts:
      type: none
      device: /opt/growi/data/es_data
      o: bind

GROWI は /opt/growi を管理ディレクトリとして、growi(dockerコンテナ)、data(データマウント先)のサブディレクトリを用意します。

$ sudo -i
# cd /opt
# mkdir growi
# chown root:docker growi
# chmod 0777 growi
# cd growi
# mkdir data
# chown root:docker data
# chmod 0777 data
# cd data
# mkdir growi_data mongo_configdb mongo_db es_data
# chmod 0777 growi_data mongo_configdb mongo_db es_data
$
$ tree /opt/growi
/opt/growi
├── data
│   ├── es_data
│   ├── growi_data
│   ├── mongo_configdb
│   └── mongo_db

/opt/growi 下の growi に GROWI を取得します。

$ cd /opt/growi
$ git clone https://github.com/weseek/growi-docker-compose.git growi

記載済み ports, environment, volumes に対する変更を docker-compose.yml に反映させます。

$ cd /opt/growi/growi
$ vi docker-compose.yml

GROWI を実行します。(※初期インストール時は sudo が必要)

$ sudo docker compose up -d

動作確認と初期設定

Ubuntu 上ブラウザで http://localhost:3000 をアクセスして、下記ログイン画面が表示されることを確認します。

GROWI-01.png

他マシンから http://IPアドレス:3000 でアクセスしても、同様にログイン画面が表示されることを確認後、ユーザ名などのアカウント情報を入力して、「作成」ボタンを押下してください。
ログインすると、上部に「アプリ設定」が表示されるので、選択します。

GROWI-02.png

下記画面となるので、「サイトURL」を設定して「更新」を押下してください。

GROWI-03.png

C# クライアント

GROWIのAPI使い方まとめ

GROWI API

GROWI は、REST API を利用して操作することが可能です。

REST API v3 - Pages - getList( _api/v3/pages/list )を利用した、ページ一覧(作成日・最終更新日・最終更新者・閲覧人数・Lile人数)取得をサンプルとします。

参考:GitHub - apiv3/pages/index.js

API Token 取得

GROWI API を利用するために必要な API Token 取得方法を記載します。
まず、左下アイコン → 設定を選択します。

GROWI-11.png

次画面で「API設定」、「API Tokenを更新」で API Token が表示されます。

GROWI-12.png

GROWI-13.png

JSON デシリアライズ

JSON デシリアライズは、現在 Microsoft が推奨している System.Text.Json を利用します。
.NET Framework の場合、System.Text.Json は標準提供されていないので、NuGet Gallery | System.Text.Json 導入が必要です。

PM> NuGet\Install-Package System.Text.Json

HttpClient

REST API クライアント実装、WebClient は .NET 6 で非推奨になったので、HttpClient を利用します。

HttpClient は、1 回インスタンス化され、アプリケーションの有効期間中に再利用されることを目的としています。 すべての要求に対して HttpClient クラスをインスタンス化すると、大量の負荷の下で使用可能なソケットの数が使い果たされます。 これにより、SocketException エラーが発生します。

IDisposable なのに、using などで都度生成/破棄するとソケット枯渇するので、初心者の方は注意してください。

有効期間管理の観点から推奨される HttpClient の使用方法を要約すると、PooledConnectionLifetime (.NET Core および .NET 5 以降) が設定された "有効期間の長い" クライアントを使うか、IHttpClientFactory で作成された "有効期間の短い" クライアントを使う必要があります。

なるほど、ガイドラインに従って IHttpClientFactory を利用することします。

DependencyInjection、IHttpClientFactory を利用するので、NuGet Gallery | Microsoft.Extensions.DependencyInjectionNuGet Gallery | Microsoft.Extensions.Http を導入します。

PM> NuGet\Install-Package Microsoft.Extensions.DependencyInjection
PM> NuGet\Install-Package Microsoft.Extensions.Http

サンプルコード

.NET Framework 4.8 と .NET 8 では、軽微な差異があるので、まず .NET Framework 4.8 のサンプルコードを記載して、次に .NET 8 の差分を記載します。

.NET Framework 4.8

まずは、ServiceCollection に対して、GrowiClient という名称の HttpClient を登録した ServiceProvider を用意します。

private static IServiceProvider MyServiceProvider = null;
var serviceCollection = new ServiceCollection();
serviceCollection.AddHttpClient("GrowiClient", client =>
{
  client.BaseAddress = new Uri("http://192.168.11.135:3000/"); // TODO - GROWI ベースアドレス
  client.DefaultRequestHeaders.Add("Accept", "application/json");
});
MyServiceProvider = serviceCollection.BuildServiceProvider();

REST API v3 - Pages - getList( _api/v3/pages/list )クライアント処理を用意します。

// REST API v3 - Pages - getList( _api/v3/pages/list )
private async Task<List<ResponsePage>> GrowiGetPageList(string accessToken)
{
  // IHttpClientFactory で GrowiClient という名前の HttpClient 取得
  var factory = MyServiceProvider?.GetService<IHttpClientFactory>();
  var httpClient = factory?.CreateClient("GrowiClient");
  if (httpClient == null)
  {
    // エラー発生 - TODO
    return null;
  }

  string baseUrl = "_api/v3/pages/list";
  var encodedToken = Uri.EscapeDataString(accessToken);
  var lstPages = new List<ResponsePage>();
  int totalCount = 0; // 全件数
  int limit = 0;      // 1回の取得件数(途中で能動的に変化はしない)
  int count = 1;      // ループ回数(1~)

  // 1回の処理は limit 件のページ取得なので、ループ処理とする
  while (true)
  {
    string targetUrl = baseUrl + $"?access_token={encodedToken}&path=/&page={count}";
    using (var request = new HttpRequestMessage(HttpMethod.Get, targetUrl))
    using (var response = await httpClient.SendAsync(request))
    {
      if (response?.IsSuccessStatusCode == true)
      {
        var content = response.Content.ReadAsStringAsync().Result;
        var obj = System.Text.Json.JsonSerializer
                    .Deserialize<ResponsePageList>(content);
        if (obj == null)
        {
          // エラー発生 - TODO
          return null;
        }
        if (limit == 0) // 初回
        {
          totalCount = obj.totalCount;
          limit = obj.limit;
        }
        if (obj.pages?.Count > 0)
        {
          foreach (ResponsePage page in obj.pages)
          {
            lstPages.Add(page);
          }
        }
      }
      else
      {
        // エラー発生 - TODO
        return null;
      }
    }
    // 残りがあるか?
    if (totalCount <= (count++ * limit))
    {
      break;
    }
  }
  return lstPages;
}
// JSON 用クラス
public class ResponsePageList
{
  public int totalCount { get; set; }          // 総件数
  public int offset { get; set; }              // 現在取得した情報の先頭からのオフセット
  public int limit { get; set; }               // 1回の取得件数
  public List<ResponsePage> pages { get; set; }
}
public class ResponsePage
{
  // 利用する項目のみ定義
  public string path { get; set; }                 // 対象ページ
  public DateTime createdAt { get; set; }          // 作成日時
  public DateTime updatedAt { get; set; }          // 更新日時
  public ResponseUser lastUpdateUser { get; set; } // 最終更新ユーザ
  public List<string> seenUsers { get; set; }      // 閲覧者一覧 - User._id のリスト
  public List<string> liker { get; set; }          // Liker一覧 - User._id のリスト
}
public class ResponseUser
{
  // 利用する項目のみ定義
  public string username { get; set; }    // ユーザID(username)
  public string name { get; set; }        // 名前
}

上記処理を以下のように呼び出します。

string accessToken = "kKJoYh8XrTsT2eMMNcfmWzljqtGWTpXweAzqotlJeug=";  // TODO - API Token

// REST API v3 - Pages - getList( _api/v3/pages/list )
var lstPages = await GrowiGetPageList(accessToken);
// TODO - CSV出力など...

.NET 8

.NET 8 では、下記差分があるので、サンプルコードの一部を書き換えます。

  • null 許容参照型の明示
    • MyServiceProvider、GrowiGetPageList 戻り値、および、JSON 用クラス
private static IServiceProvider? MyServiceProvider = null;
// REST API v3 - Pages - getList( _api/v3/pages/list )
private async Task<List<ResponsePage>?> GrowiGetPageList(string accessToken)
{
<中略>
}
// JSON 用クラス
public class ResponsePageList
{
  public int totalCount { get; set; }          // 総件数
  public int offset { get; set; }              // 現在取得した情報の先頭からのオフセット
  public int limit { get; set; }               // 1回の取得件数
  public List<ResponsePage>? pages { get; set; }
}
public class ResponsePage
{
  // 利用する項目のみ定義
  public required string path { get; set; }         // 対象ページ
  public DateTime createdAt { get; set; }           // 作成日時
  public DateTime updatedAt { get; set; }           // 更新日時
  public ResponseUser? lastUpdateUser { get; set; } // 最終更新ユーザ
  public List<string>? seenUsers { get; set; }      // 閲覧者一覧 - User._id のリスト
  public List<string>? liker { get; set; }          // Liker一覧 - User._id のリスト
}
public class ResponseUser
{
  // 利用する項目のみ定義
  public required string username { get; set; }    // ユーザID(username)
  public required string name { get; set; }        // 名前
}
1
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?