{"id":13509105,"url":"https://github.com/meddle0x53/blogit","last_synced_at":"2026-02-19T07:02:49.573Z","repository":{"id":57479974,"uuid":"78787036","full_name":"meddle0x53/blogit","owner":"meddle0x53","description":"OTP application for generating blog posts from a Git repository containing markdown files.","archived":false,"fork":false,"pushed_at":"2023-01-19T13:34:14.000Z","size":191,"stargazers_count":41,"open_issues_count":4,"forks_count":7,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-10-21T17:48:26.002Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/meddle0x53.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-12T21:06:26.000Z","updated_at":"2024-10-30T11:14:11.000Z","dependencies_parsed_at":"2023-02-11T01:45:25.424Z","dependency_job_id":null,"html_url":"https://github.com/meddle0x53/blogit","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/meddle0x53/blogit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meddle0x53%2Fblogit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meddle0x53%2Fblogit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meddle0x53%2Fblogit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meddle0x53%2Fblogit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/meddle0x53","download_url":"https://codeload.github.com/meddle0x53/blogit/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/meddle0x53%2Fblogit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29605803,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T06:47:36.664Z","status":"ssl_error","status_checked_at":"2026-02-19T06:45:47.551Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T02:01:03.014Z","updated_at":"2026-02-19T07:02:49.551Z","avatar_url":"https://github.com/meddle0x53.png","language":"Elixir","funding_links":[],"categories":["Static Page Generation","Elixir"],"sub_categories":[],"readme":"# Blogit\n\nBlogit is a blog engine back-end written in Elixir.\nIt turns a repository (by default git repository),\ncontaining markdown files into streams of blog posts, which can be queried.\nBlogit supports blog configuration in YAML, including blog title, path to custom styles and images, etc.\n\nThere is a front-end implementation in Phoenix, which uses Blogit - [BlogitWeb](https://github.com/meddle0x53/blogit_web).\nIt can be forked and configured to use any repository to build custom blog.\n\nThe [blog](https://blog.elixir-lang.bg) for the Sofia University Elixir course runs on Blogit.\n\n## Installation\n\nIt is [available in Hex](https://hex.pm/docs/publish), so the package can be installed:\n\n  1. Add `blogit` to your list of dependencies in `mix.exs`:\n\n      ```elixir\n      def deps do\n        [{:blogit, \"~\u003e 1.2\"}]\n      end\n      ```\n\n  2. Ensure `blogit` is started before your application:\n\n      ```elixir\n      def application do\n        [applications: [:blogit]]\n      end\n      ```\n\n## Configuration\n\nAn example configuration for blogit is:\n\n```elixir\nconfig :blogit,\n  repository_url: \"https://github.com/ElixirCourse/blog.git\",\n  polling: true, poll_interval: 300, languages: ~w(en bg de)\n```\n\nPossible settings are:\n* `repository_url`       |\u003e Tells Blogit the location of the repository to use to build its contents.\n* `polling`              |\u003e Tells Blogit if it should poll the repository specified through `repository_url` for changes. By default it is `true`.\n* `poll_interval`        |\u003e Used if `polling` is set to `true`. The polling for changes will happen on this interval of seconds. By default it is `10` seconds.\n* `repository_provider`  |\u003e Specified a specific implementation of the [Blogit.RepositoryProvider](https://github.com/meddle0x53/blogit/blob/master/lib/blogit/repository_provider.ex) behaviour. By default it uses `Blogit.RepositoryProviders.Git` which works with git repositories and knows how to check for changes in them.\n* `configuration_file`   |\u003e Path to YAML file in the repository, which contains configuration for the blog. By default it is `blog.yml`.\n* `languages`            |\u003e A list of language codes. By default it is `[\"en\"]`. The first language of the list is the default and primaty language. If there are alternative languages, if post source files are created in the folder `\u003crepo-root\u003e/posts/\u003clang-code\u003e`, the posts compiled from them are marked that are in the given language. They can be queried with `Blogit.list_posts(language: \"\u003clang-code\u003e\")`.\n* `max_lines_in_preview` |\u003e The maximum lines to be used from the content of the original post source to generate its preview. The preview is generated from the beginning of the content and contains maximum `max_lines_in_preview` lines. By default this value is `10`.\n\nUsing these settings a custom blog can be created from whatever repository. It is not hard to write FTP repository provider or Ecto repository provider.\n\n## Usage\n\nBlogit has a public interface which can be used to build a blog similar to [BlogitWeb](https://github.com/meddle0x53/blogit_web).\nWhen the application is configured and started, the following functions can be called:\n\n  * `Blogit.list_posts(options)`\n\n    Returns a list of `Blogit.Models.Post.Meta` structs representing posts previews in\n    the blog. The posts are sorted by their creation date, newest first.\n\n    All the markdown files in the folder `\u003crepo-root\u003e/posts` will be transformed\n    into `Blogit.Models.Post` structs and their `created_at` meta field\n    will be read using the configured `Blogit.RepositoryProvider`.\n\n    Post previews can be skipped using the `from` option, which is `0` by default.\n    The size of the returned list is `:infinity` by default (meaning \"return all post previews\"), but it can be changed\n    with the `size` option to a number.\n    By using these two options simple paging functionality can be implemented.\n\n    Another supported option is `language`. It defaults to the default language (the first one in the configured `languages` list).\n    A stream of post previews for the given `language` will be returned.\n\n    Examples:\n\n      ```elixir\n      # All post previews in the default language\n      post_previews = Blogit.list_posts()\n\n      # The first 5 post previews in the default language\n      post_previews = Blogit.list_posts(size: 5)\n\n      # The second 5 post previews in the default language\n      post_previews = Blogit.list_posts(size: 5, from: 5)\n\n      # All post previews in Bulgarian\n      post_previews = Blogit.list_posts(language: \"bg\")\n\n      # The second 5 post previews in Bulgarian\n      post_previews = Blogit.list_posts(language: \"bg\", size: 5, from: 5)\n      ```\n\n  * `Blogit.list_pinned(options)`\n\n    Returns a list of tuples. Every such tuple has the unique name of a post as first\n    element and its title as second. These tuples are sorted by the\n    last updated date of the posts they represent.\n\n    All the markdown files in the source `posts` folder will be transformed\n    into `Blogit.Models.Post` strucs and their `updated_at` meta field\n    will be read using the configured `Blogit.RepositoryProvider`.\n\n    Pinned posts are posts which have specified `pinned: true` in their meta\n    data.\n\n    These are special posts which should be easy to find in the front-end\n    implementation.\n\n    The only supported option is `language`.\n    It defaults to the default language (the first one in the configured `languages` list).\n    Pinned post tuples for the given `language` will be returned.\n\n    Examples:\n\n      ```elixir\n      # Pinned post tuples for the default language\n      Blogit.list_pinned()\n\n      # Pinned post tuples for posts in German\n      Blogit.list_pinned(language: \"de\")\n      ```\n\n  * `Blogit.filter_posts(filters, options)`\n\n    Returns a list of `Blogit.Models.Post.Meta` structs, filtered by the given `filters`.\n\n    The first argument of the function is a map of filters.\n    This map supports zero or more of the following keys:\n    * \"author\" - Used to filter posts by their `.meta.author` field.\n    * \"category\" - Used to filter posts by their `.meta.category` field.\n    * \"tags\" - Used to filter posts by their `.meta.tags` field.\n      The value for this key should a string of comma separated tags (`\"one,two,three\"`).\n    * \"year\" - Used to filter posts by their `.meta.year` field.\n    * \"month\" - Used to filter posts by their `.meta.month` field.\n    * \"q\" - A query to filter posts by their content or title. Supports text in\n      double quotes in order to search for phrases.\n\n    All these keys must be strings.\n\n    Filtered post previews can be skipped using the `from` option, which is `0` by default.\n    The size of the returned list is `:infinity` by default (meaning \"return all filtered post previews\"), but it can be changed\n    with the `size` option to a number.\n    By using these two options simple paging functionality can be implemented.\n\n    Another supported option is `language`.\n    It defaults to the default language (the first one in the configured `languages` list).\n    A stream of post previews, filtered by the given `filters` for the given `language` will be returned.\n\n    Examples:\n\n      ```elixir\n      # All the post previews in the default language, filtered by the given `filters`\n      filters = %{\"q\" =\u003e \"OTP\", \"author\" =\u003e \"meddle\", \"tags\" =\u003e \"ab\"}\n      Blogit.filter_posts(filters)\n\n      # The second two post previews in German, filtered by the given `filters`\n      filters = %{\"q\" =\u003e \"OTP\", \"author\" =\u003e \"meddle\", \"tags\" =\u003e \"ab\"}\n      Blogit.filter_posts(filters, language: \"de\", from: 2, size: 2)\n      ```\n\n  * `Blogit.posts_by_dates(options)`\n\n    Returns a list of tuples of three elements from the given list of posts.\n\n    The first element of a tuple is a year.\n    The second is a month number.\n    The third is a counter - how many posts are created during that month\n    and that year.\n\n    The tuples are sorted from the newest to the oldest, using the years and the months.\n\n    Can be used for implementing an easy-to-browse view component by years/months.\n\n    The only supported option is `language`.\n    It defaults to the default language (the first one in the configured `languages` list).\n    Statistics for the posts in the given `language` will be returned.\n\n    Examples:\n\n      ```elixir\n      # Statistics for the posts in the default language\n      posts_by_date = Blogit.posts_by_dates()\n      # [{2017, 6, 5}, {2017, 5, 1}, {2016, 5, 1}]\n\n      # Statistics for the posts in Bulgarian\n      posts_by_date = Blogit.posts_by_dates(language: \"bg\")\n      # [{2017, 5, 1}, {2016, 5, 1}]\n      ```\n\n  * `Blogit.post_by_name(name, options)`\n\n    Returns a single post by its unique identifier - its `name` field.\n    The name should be an atom. The result is in the form `{:ok, post}` if a\n    post with the given `name` exist for the given (or default) language.\n    The `post` element of that tuple is a `Blogit.Models.Post` struct.\n\n    Posts have unique names, usually constructed using the file path of their\n    source markdown file in the repository.\n\n    If there is no post with the given `name`,\n    the tuple `{:error, \"No post with name the-passed-name found.\"}` is returned.\n\n    The only supported option is `language`.\n    It defaults to the default language (the first one in the configured `languages` list).\n    Post with the given `name` in the given `language` will be returned if found.\n\n    Examples:\n\n      ```elixir\n      # A post with the name `:otp`, written in the default language\n      {:ok, post} = Blogit.post_by_name(:otp)\n      IO.puts post.name # :otp\n      IO.puts post.meta.author # The author of the post\n      IO.puts post.raw # The source markdown of the post\n\n      # A post with the name `:otp`, written in Bulgarian\n      {:ok, post} = Blogit.post_by_name(:otp, language: \"bg\")\n      ```\n\n  * `Blogit.configuration(options)`\n\n    Retrieves the blog configuration. The configuration is in the form\n    of a `Blogit.Models.Configuration` struct.\n\n    It contains title and sub-title of the blog, path to logo or background image (or both),\n    path to custom CSS file, etc..\n\n    The configuration is generated from an YAML file (by default blog.yml, stored\n    in the root of the repository of the blog). This location can be configured\n    through setting the `:configuration_file` key of the `:blogit` configuration.\n\n    The only supported option is `language`.\n    It defaults to the default language (the first one in the configured `languages` list).\n    Configuration for the given `language` will be returned.\n\n    Examples:\n\n      ```elixir\n      # The configuration of the blog for the default language\n      Blogit.configuration()\n\n      # The configuration for the German part of the blog\n      Blogit.configuration(language: \"de\")\n      ```\n\n## License\n\nThe license is standard MIT license, feel free to fork Blogit and do whatever\nyou want with it. You can also contribute to it.\n\n## Contributions\n\nJust fork the Blogit repository and create a PR. You can also create issues\nwith features you wish we support. You can propose style changes on the code and\nadditional tests.\n\nAll kinds of contributions are welcome!\n\n## How to create your own blog?\n\nBlogit has a simple Phoenix front-end application : [BlogitWeb](https://github.com/meddle0x53/blogit_web).\n\nFollow these steps to create your own blog (read more at [BlogitWeb's README](https://github.com/meddle0x53/blogit_web/blob/master/README.md)):\n  1. Fork [BlogitWeb](https://github.com/meddle0x53/blogit_web) project.\n  2. Modify its `config/prod.exs` configuration:\n\n      ```elixir\n      config :blogit,\n        repository_url: \"\u003cpath-to-a-git-repository-with-your-posts\u003e\",\n        polling: true, poll_interval: 300\n      ```\n\n  3. Recompile the dependencies, so the configuration is updated:\n\n      ```bash\n      mix deps.get\n      mix deps.clean\n      mix deps.compile\n      ```\n\n  4. Deploy it somewhere.\n\n     BlogitWeb depends on `distillery` and has a little bash script in its root,\n     called `build_release.sh`. Just run it and it will build a release.\n     You can also run it like this : `./build_release.sh --upgrade` in order to\n     create updated release. Follow `distillery`'s notes when deploying.'\n\n     BlogitWeb has a `Dockerfile.build` file which can be used to make a build in\n     `Docker` if you don't have `Elixir` installed. It has a `Dockerfile` too so\n     you can try it locally.\n\n     There is a template `edeliver` configuration too.\n\n  5. When it is deployed just add new markdown posts to your repository specified\n     in the configuration and it will be published automatically.\n\n## Features to be implemented\n\n  * Pages (for example 'about me') and specific streams of posts, presented as pages.\n  * Slides. If we have a folder containing a specifically formated markdown (?) files, they could be\n    turned into slides available on Blogit.\n  * Different front-ends for Blogit. Not only BlogtWeb. Also let's keep BlogitWeb up to date, tested and documented. For now it is a bit messy, but usable.\n  * Multiple source formats for posts, not only MARKDOWN.\n  * Additional repository providers.\n\n## Contact me\n\nI'm Nikolay Tsvetinov (meddle) from [elixir-lang.bg](https://blog.elixir-lang.bg/posts).\n\nYou can find me at:\n* [Twitter](https://twitter.com/ntzvetinov)\n* [Github](https://github.com/meddle0x53)\n* [Gmail](mailto:n.tzvetinov@gmail.com)\n* [elixir-lang.bg](mailto:n.tzvetinov@elixir-lang.bg)\n* [My blog, implemented using Blogit](http://themeddle.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeddle0x53%2Fblogit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmeddle0x53%2Fblogit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeddle0x53%2Fblogit/lists"}