Perlでgrowiのバックアップファイルからmarkdownを生成する(version1くらい)

研究室ではwikiとしてgrowiを使っています。

growi.cr.ie.u-ryukyu.ac.jp

まぁ便利なのですが、growiはデータベースがmongoDBなので、心もとないという意見や、せっかく階層構造を持っているのでmarkdown単体で保存しておきたいという意見が出ています。

もともとAPI経由でやろうとしていましたが、まぁせっかくなのでbackupファイルからmarkdownを生成してみます。

記事のbackupファイルの取得

growiでは記事はrevisionsというコレクションで登録されています。 growiのwebページからbackupを作成するか、dockerで動かしている場合は次のようなコマンドでrevisonsのjsonを取得します。

$docker exec growi_mongo_1 mongoexport -d growi -c revisions --out revision_back_1013.json
$docker  cp growi_mongo_1:revision_back_1013.json .
$docker exec growi_mongo_1 rm revision_back_1013.json

ここで取得したjsonですが、それぞれの投稿データは次のようなスキーマになっています。

  {
    "_id": "5ecce2e5fc19b9004a86ec47",
    "format": "markdown",
    "createdAt": "2020-05-26T09:35:33.830Z",
    "path": "/user/anatofuz/note/2020/05/26",
    "body": "ここに内容が入る",
    "author": "5df5ef37d744a60045dd1524",
    "hasDiffToPrev": true,
    "__v": 0
  }

注目すべきはcreatedAtはタイムスタンプになっており、authorはgrowiのuserのidとなっています。

authorが具体的に何かはrevisionsだけでは決まりませんので、とりあえず今回は放置します。

pathはgrowiのエントリのタイトルです。これはunixのファイルパスと対応してそうなので、生成したmarkdownはこのパスを利用します。

bodyはエントリの内容ですので、bodyをpathに書かれた場所に書き込む方針を取ります。

markdown自体はgit/hgなどのバージョン管理ツールで管理するのを想定するので、createdAtは最新のものを1つだけ使うようにしてみます。

Perlで軽く書く

というわけでPerlで書いてみました。

みんな大好きPath::Tinyを使っています。

metacpan.org

#!/usr/bin/env perl
use strict;
use warnings;

use utf8;
use Encode;
use JSON;
use Path::Tiny;

my $json_file = shift or die 'require json file';
my $revision = decode_json(path($json_file)->slurp);


my $paths;

for my $elem (@$revision) {
   push(@{$paths->{$elem->{path}}}, $elem);
}

for my $path (keys %$paths) {
    my $elems = $paths->{$path};
    print encode_utf8 "$path\n";

    my @sorted_elems   = sort { $b->{createdAt} cmp $a->{createdAt}}  @$elems;
    my $latest_elem    = shift @sorted_elems;

    my $emit_file_path = path("./emit/$path.md");
    $emit_file_path->touchpath;
    $emit_file_path->spew_utf8($latest_elem->{body});
}

__END__
  {
    "_id": "5ecce2e5fc19b9004a86ec47",
    "format": "markdown",
    "createdAt": "2020-05-26T09:35:33.830Z",
    "path": "/user/anatofuz/note/2020/05/26",
    "body": "",
    "author": "5df5ef37d744a60045dd1524",
    "hasDiffToPrev": true,
    "__v": 0
  }

$perl parse.pl revisons.json

の様に使います。

今回は/user/anatofuz/note/2020/05/26の場合は./emit/user/anatofuz/note/2020/05/26.mdというファイルが生成されるようにしてみました。

実際に研究室のGrowiのbackupをもとに実行すると

$ ls emit
611/         Agda.md      Christie/    FileSystem/  Haskell/     Linux/       Sandbox/     software/    user/
611.md       CbC/         Christie.md  Gears/       Haskell.md   Raku/        Sandbox.md   software.md  user.md
Agda/        CbC.md       Events/      Gears.md     Linda.md     Raku.md      growi.md     trash/

の様になっており、例えばRaku.mdを見ると

# Raku

ちょっと前までPerl6と呼ばれていたプログラミング言語

# Docs

- 公式Document
    - https://docs.raku.org/
        - `docs.perl6.org`のものは古いので見ない

# contents

$lsx(/Raku)% 

と無事取れていますね!!!

TODO

まぁ色々雑なのでTODOが多々あります

  • 作者の情報を取得する
  • Hugo等の静的サイトジェネレーターとの組み合わせを考えて、メタ情報をつける
  • 生成パスがこれで良いのか問題
  • できればワンバイナリかfatpackしたい

ということでversion1くらいの話です。まぁこういうの書くの楽しいですよね