바둑을 배우려는 시도 - 유령이 휴고에게 1

49410 단어 goghosthugo

소개



당신은 아마도 이것을 모르지만 shindakun.net은 몇 달 동안 효과적으로 오프라인 상태였습니다. 현재 방문하면 흰색 페이지와 팝업만 표시되며 콘텐츠에 액세스할 수 없습니다. 이것은 새 서버로 업그레이드하고 내가 선택한 플랫폼으로 Ghost에서 멀어지는 부작용입니다.

나는 (d) Ghost를 좋아하지만 Digital Ocean의 작은 인스턴스에서 여러 사이트를 효과적으로 실행하기에는 너무 커졌습니다. 사이트를 백업하고 실행하고 싶지만 이번에는 Hugo 을 사용합니다. 이것은 새로운 ATLG 게시물 시리즈를 만들 수 있는 좋은 기회인 것 같습니다. 게다가 Go를 쓴 지 꽤 되었습니다.

포스트 데이터



원래 서버를 종료하기 전에 Ghost 데이터베이스의 복사본을 JSON으로 내보냈습니다. 사이트의 콘텐츠를 구성하는 데 필요한 Markdown 파일을 구축하는 데 이것을 사용할 수 있어야 합니다. 다음은 첫 번째 게시물입니다.

{
    "id": "60710b90705967038fe662d6",
    "uuid": "71ba3d71-ac18-4f33-82f7-1962baa83a07",
    "title": "db test",
    "slug": "db-test",
    "mobiledoc": "{\"version\":\"0.3.1\",\"markups\":[],\"atoms\":[],\"cards\":[[\"markdown\",{\"cardName\":\"card-markdown\",\"markdown\":\"\\n<strike>This is a db test</strike>.\\n\"}]],\"sections\":[[10,0]],\"ghostVersion\":\"3.0\"}",
    "html": "<!--kg-card-begin: markdown--><p><strike>This is a db test</strike>.</p>\n<!--kg-card-end: markdown-->",
    "comment_id": "2",
    "plaintext": "This is a db test.",
    "feature_image": null,
    "featured": 0,
    "type": "post",
    "status": "published",
    "locale": null,
    "visibility": "public",
    "email_recipient_filter": "none",
    "author_id": "60710b8d705967038fe66214",
    "created_at": "2004-08-09T19:11:20.000Z",
    "updated_at": "2004-08-09T19:11:20.000Z",
    "published_at": "2004-08-09T19:11:20.000Z",
    "custom_excerpt": null,
    "codeinjection_head": null,
    "codeinjection_foot": null,
    "custom_template": null,
    "canonical_url": null
},


예, 2004년으로 거슬러 올라가는 게시물이 있습니다! 그때 저는 PHP로 나만의 맞춤형 블로그 소프트웨어를 작성했습니다. 좋은 시간이었지만 살아남은 코드가 하나도 없었습니다. Go에서만 다시 시도하고 싶을 뻔했지만… 그런 충동을 참을 것입니다.

빠르고 더러운



좋습니다. JSON을 읽고 단일 게시물을 출력하는 프로그램을 작성해 봅시다. 먼저 "데이터베이스"에 매핑되는 구조체가 필요합니다. 우리는 단순히 JSON을 awesomeJSON to Go converter에 넣음으로써 이것을 얻습니다.

type GhostDatabase struct {
    Db []struct {
        Meta struct {
            ExportedOn int64 `json:"exported_on"`
            Version string `json:"version"`
        } `json:"meta"`
        Data struct {
            Posts []struct {
                ID string `json:"id"`
                UUID string `json:"uuid"`
                Title string `json:"title"`
                Slug string `json:"slug"`
                Mobiledoc string `json:"mobiledoc"`
                HTML string `json:"html"`
                CommentID string `json:"comment_id"`
                Plaintext string `json:"plaintext"`
                FeatureImage interface{} `json:"feature_image"`
                Featured int `json:"featured"`
                Type string `json:"type"`
                Status string `json:"status"`
                Locale interface{} `json:"locale"`
                Visibility string `json:"visibility"`
                EmailRecipientFilter string `json:"email_recipient_filter"`
                AuthorID string `json:"author_id"`
                CreatedAt time.Time `json:"created_at"`
                UpdatedAt time.Time `json:"updated_at"`
                PublishedAt time.Time `json:"published_at"`
                CustomExcerpt interface{} `json:"custom_excerpt"`
                CodeinjectionHead interface{} `json:"codeinjection_head"`
                CodeinjectionFoot interface{} `json:"codeinjection_foot"`
                CustomTemplate interface{} `json:"custom_template"`
                CanonicalURL interface{} `json:"canonical_url"`
            } `json:"posts"`
            PostsAuthors []struct {
                ID string `json:"id"`
                PostID string `json:"post_id"`
                AuthorID string `json:"author_id"`
                SortOrder int `json:"sort_order"`
            } `json:"posts_authors"`
            PostsMeta []interface{} `json:"posts_meta"`
            PostsTags []struct {
                ID string `json:"id"`
                PostID string `json:"post_id"`
                TagID string `json:"tag_id"`
                SortOrder int `json:"sort_order"`
            } `json:"posts_tags"`
            Roles []struct {
                ID string `json:"id"`
                Name string `json:"name"`
                Description string `json:"description"`
                CreatedAt time.Time `json:"created_at"`
                UpdatedAt time.Time `json:"updated_at"`
            } `json:"roles"`
            RolesUsers []struct {
                ID string `json:"id"`
                RoleID string `json:"role_id"`
                UserID string `json:"user_id"`
            } `json:"roles_users"`
            Settings []struct {
                ID string `json:"id"`
                Group string `json:"group"`
                Key string `json:"key"`
                Value string `json:"value"`
                Type string `json:"type"`
                Flags interface{} `json:"flags"`
                CreatedAt time.Time `json:"created_at"`
                UpdatedAt time.Time `json:"updated_at"`
            } `json:"settings"`
            Tags []struct {
                ID string `json:"id"`
                Name string `json:"name"`
                Slug string `json:"slug"`
                Description interface{} `json:"description"`
                FeatureImage interface{} `json:"feature_image"`
                ParentID interface{} `json:"parent_id"`
                Visibility string `json:"visibility"`
                OgImage interface{} `json:"og_image"`
                OgTitle interface{} `json:"og_title"`
                OgDescription interface{} `json:"og_description"`
                TwitterImage interface{} `json:"twitter_image"`
                TwitterTitle interface{} `json:"twitter_title"`
                TwitterDescription interface{} `json:"twitter_description"`
                MetaTitle interface{} `json:"meta_title"`
                MetaDescription interface{} `json:"meta_description"`
                CodeinjectionHead interface{} `json:"codeinjection_head"`
                CodeinjectionFoot interface{} `json:"codeinjection_foot"`
                CanonicalURL interface{} `json:"canonical_url"`
                AccentColor interface{} `json:"accent_color"`
                CreatedAt time.Time `json:"created_at"`
                UpdatedAt time.Time `json:"updated_at"`
            } `json:"tags"`
            Users []struct {
                ID string `json:"id"`
                Name string `json:"name"`
                Slug string `json:"slug"`
                Password string `json:"password"`
                Email string `json:"email"`
                ProfileImage string `json:"profile_image"`
                CoverImage interface{} `json:"cover_image"`
                Bio interface{} `json:"bio"`
                Website interface{} `json:"website"`
                Location interface{} `json:"location"`
                Facebook interface{} `json:"facebook"`
                Twitter interface{} `json:"twitter"`
                Accessibility string `json:"accessibility"`
                Status string `json:"status"`
                Locale interface{} `json:"locale"`
                Visibility string `json:"visibility"`
                MetaTitle interface{} `json:"meta_title"`
                MetaDescription interface{} `json:"meta_description"`
                Tour interface{} `json:"tour"`
                LastSeen time.Time `json:"last_seen"`
                CreatedAt time.Time `json:"created_at"`
                UpdatedAt time.Time `json:"updated_at"`
            } `json:"users"`
        } `json:"data"`
    } `json:"db"`
}


완벽한. 모든 구조체가 반드시 필요한 것은 아니지만 지금은 그대로 두겠습니다. 내가 게시물에 대한 태그를 당기기 위해 무언가를 쓸 수도 있다는 것을 누가 알겠습니까?

파일 읽기



먼저 파일을 열겠습니다. 이것은 실제로 빠르고 지저분한 버전이므로 나중에 적절한 오류 처리를 추가할 수 있습니다.

    file, err := os.Open("shindakun-dot-net.ghost.2022-03-18-22-02-58.json")
    if err != nil {
        fmt.Println(err)
    }


열리면 파일을 []byte 로 메모리로 읽어야 합니다.

    b, err := io.ReadAll(file)
    if err != nil {
        fmt.Println(err)
    }


이제 데이터베이스 변수 db 를 선언합니다. json.Unmarshal()를 사용하여 JSON을 사용할 구조체로 변환합니다.

    var db GhostDatabase

    err = json.Unmarshal(b, &db)
    if err != nil {
        fmt.Println(err)
    }


마지막으로 첫 번째 게시물의 텍스트를 인쇄합니다.

    fmt.Printf("%#v", db.Db[0].Data.Posts[0].HTML)


이것은 우리에게 출력을 남깁니다.

<!--kg-card-begin: markdown--><p><strike>This is a db test</strike>.</p>\n<!--kg-card-end: markdown-->


다음번



우리 프로그램의 이 부분은 상대적으로 쉽습니다. 하지만 우리는 HTML을 원하지 않습니다. 저는 오히려 Markdown을 mobiledoc 값에서 빼내고 싶습니다.

"mobiledoc": "{\"version\":\"0.3.1\",\"markups\":[],\"atoms\":[],\"cards\":[[\"markdown\",{\"cardName\":\"card-markdown\",\"markdown\":\"\\n<strike>This is a db test</strike>.\\n\"}]],\"sections\":[[10,0]],\"ghostVersion\":\"3.0\"}",


운 좋게도 포함된 JSON 개체일 뿐입니다. 이스케이프된 따옴표를 제거하는 개체를 정리해야 할 것 같습니다. 일단 정리되면 다음과 같이 보일 것입니다.

{
    "version": "0.3.1",
    "markups": [],
    "atoms": [],
    "cards": [
        [
            "markdown",
            {
                "cardName": "card-markdown",
                "markdown": "\n<strike>This is a db test</strike>.\n"
            }
        ]
    ],
    "sections": [
        [
            10,
            0
        ]
    ],
    "ghostVersion": "3.0"
}


데이터베이스를 자세히 살펴보지는 않았지만 Ghost에서 게시물을 작성할 때 모두 단일card-markdown에 있었으면 합니다. 오 글쎄요, 우리는 결국 그 다리를 건너게 될 것입니다.

다음 시간까지!



이 게시물을 즐기십니까?


How about buying me a coffee?



코드 목록




package main

import (
    "encoding/json"
    "fmt"
    "io"
    "os"
    "time"
)

type GhostDatabase struct {
    Db []struct {
        Meta struct {
            ExportedOn int64 `json:"exported_on"`
            Version string `json:"version"`
        } `json:"meta"`
        Data struct {
            Posts []struct {
                ID string `json:"id"`
                UUID string `json:"uuid"`
                Title string `json:"title"`
                Slug string `json:"slug"`
                Mobiledoc string `json:"mobiledoc"`
                HTML string `json:"html"`
                CommentID string `json:"comment_id"`
                Plaintext string `json:"plaintext"`
                FeatureImage interface{} `json:"feature_image"`
                Featured int `json:"featured"`
                Type string `json:"type"`
                Status string `json:"status"`
                Locale interface{} `json:"locale"`
                Visibility string `json:"visibility"`
                EmailRecipientFilter string `json:"email_recipient_filter"`
                AuthorID string `json:"author_id"`
                CreatedAt time.Time `json:"created_at"`
                UpdatedAt time.Time `json:"updated_at"`
                PublishedAt time.Time `json:"published_at"`
                CustomExcerpt interface{} `json:"custom_excerpt"`
                CodeinjectionHead interface{} `json:"codeinjection_head"`
                CodeinjectionFoot interface{} `json:"codeinjection_foot"`
                CustomTemplate interface{} `json:"custom_template"`
                CanonicalURL interface{} `json:"canonical_url"`
            } `json:"posts"`
            PostsAuthors []struct {
                ID string `json:"id"`
                PostID string `json:"post_id"`
                AuthorID string `json:"author_id"`
                SortOrder int `json:"sort_order"`
            } `json:"posts_authors"`
            PostsMeta []interface{} `json:"posts_meta"`
            PostsTags []struct {
                ID string `json:"id"`
                PostID string `json:"post_id"`
                TagID string `json:"tag_id"`
                SortOrder int `json:"sort_order"`
            } `json:"posts_tags"`
            Roles []struct {
                ID string `json:"id"`
                Name string `json:"name"`
                Description string `json:"description"`
                CreatedAt time.Time `json:"created_at"`
                UpdatedAt time.Time `json:"updated_at"`
            } `json:"roles"`
            RolesUsers []struct {
                ID string `json:"id"`
                RoleID string `json:"role_id"`
                UserID string `json:"user_id"`
            } `json:"roles_users"`
            Settings []struct {
                ID string `json:"id"`
                Group string `json:"group"`
                Key string `json:"key"`
                Value string `json:"value"`
                Type string `json:"type"`
                Flags interface{} `json:"flags"`
                CreatedAt time.Time `json:"created_at"`
                UpdatedAt time.Time `json:"updated_at"`
            } `json:"settings"`
            Tags []struct {
                ID string `json:"id"`
                Name string `json:"name"`
                Slug string `json:"slug"`
                Description interface{} `json:"description"`
                FeatureImage interface{} `json:"feature_image"`
                ParentID interface{} `json:"parent_id"`
                Visibility string `json:"visibility"`
                OgImage interface{} `json:"og_image"`
                OgTitle interface{} `json:"og_title"`
                OgDescription interface{} `json:"og_description"`
                TwitterImage interface{} `json:"twitter_image"`
                TwitterTitle interface{} `json:"twitter_title"`
                TwitterDescription interface{} `json:"twitter_description"`
                MetaTitle interface{} `json:"meta_title"`
                MetaDescription interface{} `json:"meta_description"`
                CodeinjectionHead interface{} `json:"codeinjection_head"`
                CodeinjectionFoot interface{} `json:"codeinjection_foot"`
                CanonicalURL interface{} `json:"canonical_url"`
                AccentColor interface{} `json:"accent_color"`
                CreatedAt time.Time `json:"created_at"`
                UpdatedAt time.Time `json:"updated_at"`
            } `json:"tags"`
            Users []struct {
                ID string `json:"id"`
                Name string `json:"name"`
                Slug string `json:"slug"`
                Password string `json:"password"`
                Email string `json:"email"`
                ProfileImage string `json:"profile_image"`
                CoverImage interface{} `json:"cover_image"`
                Bio interface{} `json:"bio"`
                Website interface{} `json:"website"`
                Location interface{} `json:"location"`
                Facebook interface{} `json:"facebook"`
                Twitter interface{} `json:"twitter"`
                Accessibility string `json:"accessibility"`
                Status string `json:"status"`
                Locale interface{} `json:"locale"`
                Visibility string `json:"visibility"`
                MetaTitle interface{} `json:"meta_title"`
                MetaDescription interface{} `json:"meta_description"`
                Tour interface{} `json:"tour"`
                LastSeen time.Time `json:"last_seen"`
                CreatedAt time.Time `json:"created_at"`
                UpdatedAt time.Time `json:"updated_at"`
            } `json:"users"`
        } `json:"data"`
    } `json:"db"`
}

func main() {
    fmt.Println("ghost2hugo")

    file, err := os.Open("shindakun-dot-net.ghost.2022-03-18-22-02-58.json")
    if err != nil {
        fmt.Println(err)
    }

    b, err := io.ReadAll(file)
    if err != nil {
        fmt.Println(err)
    }

    var db GhostDatabase

    err = json.Unmarshal(b, &db)
    if err != nil {
        fmt.Println(err)
    }

    fmt.Printf("%#v", db.Db[0].Data.Posts[0].HTML)
}

좋은 웹페이지 즐겨찾기