Compare commits
186 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2acf391f7d | |||
| a8b466c8db | |||
| 7bd7fa90c4 | |||
| 33b8e61f42 | |||
| 7b3ae70073 | |||
| cd3ffa479c | |||
| 99cbe51cac | |||
| e3e3346fd3 | |||
| 0e69643c66 | |||
| e930ed0f2f | |||
| 4cd29e188d | |||
| 7ffcc1a83b | |||
| 8d37136815 | |||
| 2f1c45293e | |||
| 464bd0ccef | |||
| bb04c90f4a | |||
| a0fb32aa0f | |||
| a069ca7961 | |||
| c091bac4aa | |||
| a66e3fa1a3 | |||
| 8f4170a4b7 | |||
| e3e24d561d | |||
| f721c98e33 | |||
| 88ef311141 | |||
| c3d25b78e4 | |||
| a1c6f518aa | |||
| 01a666a42e | |||
| 982c65f9ed | |||
| edbf7ec02f | |||
| d684eca9b3 | |||
| 0357345d02 | |||
| ea8dddd6fb | |||
| 11364b0047 | |||
| 160441293e | |||
| 9852e338b0 | |||
| 50b53f6371 | |||
| 5e0b39a1fb | |||
| f06a42cb69 | |||
| 1c68d60f2b | |||
| 6c188e779b | |||
| 5a93cb9b86 | |||
| 95f097234d | |||
| dc3104527b | |||
| 68e056d3ad | |||
| 30c159be2c | |||
| 82276dbb8a | |||
| 31adea59f4 | |||
| 09d1c94a92 | |||
| 74a1ba43cd | |||
| 03a9269fa3 | |||
| 9dc892c1d4 | |||
| d69ee3326e | |||
| ff1e7d5272 | |||
| 06f524f644 | |||
| 5a0ff70502 | |||
| e8ee51606b | |||
| b7ce8ad551 | |||
| 54c71e5ef9 | |||
| 85554b9a65 | |||
| c3a8d7c481 | |||
| eceb434a10 | |||
| 5942fde518 | |||
| 9a077ba3b2 | |||
| 19ce8babd9 | |||
| f50d84238d | |||
| c110f5bb79 | |||
| 24f1839498 | |||
| 77714e38b2 | |||
| 8e37d4e1c0 | |||
| 639a1725d1 | |||
| 62e4072eb5 | |||
| 0042cd2ff9 | |||
| 349efbe2b5 | |||
| 5b44a4c64e | |||
| ce7ed6540a | |||
| 698d87c243 | |||
| d37f79a418 | |||
| f1f5a036a9 | |||
| e8c585d3ce | |||
| 966b40182c | |||
| cf5c2d0ec6 | |||
| 7ba501eee1 | |||
| f85b0f913e | |||
| 58b1045d7b | |||
| 42c7da8e74 | |||
| b9c4490727 | |||
| 80485e0efd | |||
| 068308047c | |||
| 1f6b3fffb8 | |||
| 7059a5bee4 | |||
| 4a11f0c850 | |||
| 850d47d676 | |||
| f073272f1f | |||
| 87b05cf341 | |||
| ab3917adcc | |||
| 9d6fc1a6d8 | |||
| b33faddcb5 | |||
| 1e1ba9a237 | |||
| c03611262b | |||
| dd1ed4d0d0 | |||
| 345840c543 | |||
| 34cf084465 | |||
| 558425fa80 | |||
| a01c9dd184 | |||
| 6aca7be258 | |||
| df6ac5c78d | |||
| 51fe1acb9c | |||
| 36de09ac1c | |||
| 0a2917809f | |||
| 80d1a46f6c | |||
| da578461fc | |||
| 9438467d67 | |||
| 7794119258 | |||
| 1c126f2e82 | |||
| e329944c13 | |||
| 86d11b759a | |||
| 1d9ac4dbce | |||
| 5baad750f4 | |||
| 07c4796861 | |||
| e82a1b5ce8 | |||
| 42f4b533b1 | |||
| 59fda443de | |||
| 08cdbcdd82 | |||
| 4ab4cad773 | |||
| dd616fda41 | |||
| fd8c860d35 | |||
| 27e59a86bc | |||
| 8d08147c91 | |||
| 81df71d4e9 | |||
| 1b91ea1be3 | |||
| 346bb1877c | |||
| c2c9bdad43 | |||
| 41bb9fdef4 | |||
| 81bdcf6259 | |||
| 8791931369 | |||
| d3db8448fb | |||
|
|
25554ab5ba | ||
| e009b14513 | |||
|
|
464510ccee | ||
|
|
768c5894b8 | ||
|
|
125f681a96 | ||
| 5e8c81991b | |||
| 953ae20c0f | |||
| afef118c4f | |||
| 5cb98900a8 | |||
| aeb2057daf | |||
| 2bc5d2d37e | |||
| e4917f7815 | |||
| c17f014a0b | |||
| 46a04a65fd | |||
| 1b4df0c60d | |||
| b731264228 | |||
| b8f58ec183 | |||
| 035fadc522 | |||
| 249ad00d5e | |||
| 9389291197 | |||
| ea58b46dff | |||
| bb07412482 | |||
| feda9f8675 | |||
| 748f9ea794 | |||
| e4cc95f65c | |||
| a5454583fb | |||
| 0d20295d3f | |||
| a81f284f29 | |||
| 9c09052c9f | |||
| f42870f595 | |||
| a956b3ae95 | |||
| 2272ba9ebf | |||
| 389df264b9 | |||
| 382147a3c3 | |||
| 99c58af561 | |||
| 8894ed0ccf | |||
| a7e3a02666 | |||
| 7da38814ad | |||
| dd3a25e3eb | |||
| 60a4b6fc36 | |||
| c11e8b384c | |||
| 72dc80dbba | |||
| 745f828480 | |||
| 3afd986181 | |||
|
|
619d467e20 | ||
|
|
6ac7927e37 | ||
| 82d378ccf9 | |||
|
|
296311c828 | ||
|
|
6d5743563a | ||
|
|
88feecfe3f |
1
.frontmatter/database/mediaDb.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
||||
1
.frontmatter/database/pinnedItemsDb.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
||||
1
.frontmatter/database/taxonomyDb.json
Normal file
@ -0,0 +1 @@
|
||||
{"taxonomy":{"tags":["comment-system","hello","principles","privacy","remark42","self-hosted","test","world"],"categories":[]}}
|
||||
14
.github/workflows/pages-deploy.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# submodules: true
|
||||
@ -37,12 +37,12 @@ jobs:
|
||||
|
||||
- name: Setup Pages
|
||||
id: pages
|
||||
uses: actions/configure-pages@v3
|
||||
uses: actions/configure-pages@v4
|
||||
|
||||
- name: Setup Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 3 # reads from a '.ruby-version' or '.tools-version' file if 'ruby-version' is omitted
|
||||
ruby-version: 3.3
|
||||
bundler-cache: true
|
||||
|
||||
- name: Build site
|
||||
@ -52,10 +52,12 @@ jobs:
|
||||
|
||||
- name: Test site
|
||||
run: |
|
||||
bundle exec htmlproofer _site --disable-external --check-html --allow_hash_href
|
||||
bundle exec htmlproofer _site \
|
||||
\-\-disable-external \
|
||||
\-\-ignore-urls "/^http:\/\/127.0.0.1/,/^http:\/\/0.0.0.0/,/^http:\/\/localhost/"
|
||||
|
||||
- name: Upload site artifact
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: "_site${{ steps.pages.outputs.base_path }}"
|
||||
|
||||
@ -68,4 +70,4 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v2
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
7
.gitignore
vendored
@ -5,6 +5,7 @@ Gemfile.lock
|
||||
|
||||
# Jekyll cache
|
||||
.jekyll-cache
|
||||
.jekyll-metadata
|
||||
_site
|
||||
|
||||
# RubyGems
|
||||
@ -16,7 +17,11 @@ package-lock.json
|
||||
|
||||
# IDE configurations
|
||||
.idea
|
||||
.vscode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/tasks.json
|
||||
|
||||
# Misc
|
||||
_sass/vendors
|
||||
assets/js/dist
|
||||
|
||||
15
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"[markdown]": {
|
||||
"editor.fontSize": 14,
|
||||
"editor.lineHeight": 26,
|
||||
"editor.wordWrap": "wordWrapColumn",
|
||||
"editor.wordWrapColumn": 64,
|
||||
"editor.lineNumbers": "off",
|
||||
"editor.quickSuggestions": {
|
||||
"comments": "off",
|
||||
"strings": "off",
|
||||
"other": "off"
|
||||
},
|
||||
"editor.minimap.enabled": false
|
||||
}
|
||||
}
|
||||
26
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Run Jekyll Server",
|
||||
"type": "shell",
|
||||
"command": "./tools/run.sh",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"detail": "Runs the Jekyll server with live reload."
|
||||
},
|
||||
{
|
||||
"label": "Build Jekyll Site",
|
||||
"type": "shell",
|
||||
"command": "./tools/test.sh",
|
||||
"group": {
|
||||
"kind": "build"
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"detail": "Build the Jekyll site for production."
|
||||
}
|
||||
]
|
||||
}
|
||||
13
Gemfile
@ -2,10 +2,10 @@
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "jekyll-theme-chirpy", "~> 6.1"
|
||||
gem "jekyll-theme-chirpy", "~> 7.2", ">= 7.2.2"
|
||||
|
||||
group :test do
|
||||
gem "html-proofer", "~> 3.18"
|
||||
gem "html-proofer", "~> 5.0", group: :test
|
||||
end
|
||||
|
||||
# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
|
||||
@ -16,13 +16,8 @@ platforms :mingw, :x64_mingw, :mswin, :jruby do
|
||||
end
|
||||
|
||||
# Performance-booster for watching directories on Windows
|
||||
gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
|
||||
gem "wdm", "~> 0.2.0", :platforms => [:mingw, :x64_mingw, :mswin]
|
||||
|
||||
# Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem
|
||||
# do not have a Java counterpart.
|
||||
gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby]
|
||||
|
||||
# Lock jekyll-sass-converter to 2.x on Linux-musl
|
||||
if RUBY_PLATFORM =~ /linux-musl/
|
||||
gem "jekyll-sass-converter", "~> 2.0"
|
||||
end
|
||||
gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby]
|
||||
42
README.md
@ -1,10 +1,18 @@
|
||||
# Chirpy Starter [](https://rubygems.org/gems/jekyll-theme-chirpy) [][mit]
|
||||
# Chirpy Starter
|
||||
|
||||
When installing the [**Chirpy**][chirpy] theme through [RubyGems.org][gem], Jekyll can only read files in the folders `/_data`, `/_layouts`, `/_includes`, `/_sass` and `/assets`, as well as a small part of options of the `/_config.yml` file from the theme's gem. If you have ever installed this theme gem, you can use the command `bundle info --path jekyll-theme-chirpy` to locate these files.
|
||||
[][gem]
|
||||
[][mit]
|
||||
|
||||
The Jekyll team claims that this is to leave the ball in the user’s court, but this also results in users not being able to enjoy the out-of-the-box experience when using feature-rich themes.
|
||||
When installing the [**Chirpy**][chirpy] theme through [RubyGems.org][gem], Jekyll can only read files in the folders
|
||||
`_data`, `_layouts`, `_includes`, `_sass` and `assets`, as well as a small part of options of the `_config.yml` file
|
||||
from the theme's gem. If you have ever installed this theme gem, you can use the command
|
||||
`bundle info --path jekyll-theme-chirpy` to locate these files.
|
||||
|
||||
To fully use all the features of **Chirpy**, you need to copy the other critical files from the theme's gem to your Jekyll site. The following is a list of targets:
|
||||
The Jekyll team claims that this is to leave the ball in the user’s court, but this also results in users not being
|
||||
able to enjoy the out-of-the-box experience when using feature-rich themes.
|
||||
|
||||
To fully use all the features of **Chirpy**, you need to copy the other critical files from the theme's gem to your
|
||||
Jekyll site. The following is a list of targets:
|
||||
|
||||
```shell
|
||||
.
|
||||
@ -14,25 +22,16 @@ To fully use all the features of **Chirpy**, you need to copy the other critical
|
||||
└── index.html
|
||||
```
|
||||
|
||||
To save you time, and also in case you lose some files while copying, we extract those files/configurations of the latest version of the **Chirpy** theme and the [CD][CD] workflow to here, so that you can start writing in minutes.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Follow the instructions in the [Jekyll Docs](https://jekyllrb.com/docs/installation/) to complete the installation of the basic environment. [Git](https://git-scm.com/) also needs to be installed.
|
||||
|
||||
## Installation
|
||||
|
||||
Sign in to GitHub and [**use this template**][use-template] to generate a brand new repository and name it `USERNAME.github.io`, where `USERNAME` represents your GitHub username.
|
||||
|
||||
Then clone it to your local machine and run:
|
||||
|
||||
```
|
||||
$ bundle
|
||||
```
|
||||
To save you time, and also in case you lose some files while copying, we extract those files/configurations of the
|
||||
latest version of the **Chirpy** theme and the [CD][CD] workflow to here, so that you can start writing in minutes.
|
||||
|
||||
## Usage
|
||||
|
||||
Please see the [theme's docs](https://github.com/cotes2020/jekyll-theme-chirpy#documentation).
|
||||
Check out the [theme's docs](https://github.com/cotes2020/jekyll-theme-chirpy/wiki).
|
||||
|
||||
## Contributing
|
||||
|
||||
This repository is automatically updated with new releases from the theme repository. If you encounter any issues or want to contribute to its improvement, please visit the [theme repository][chirpy] to provide feedback.
|
||||
|
||||
## License
|
||||
|
||||
@ -40,6 +39,5 @@ This work is published under [MIT][mit] License.
|
||||
|
||||
[gem]: https://rubygems.org/gems/jekyll-theme-chirpy
|
||||
[chirpy]: https://github.com/cotes2020/jekyll-theme-chirpy/
|
||||
[use-template]: https://github.com/cotes2020/chirpy-starter/generate
|
||||
[CD]: https://en.wikipedia.org/wiki/Continuous_deployment
|
||||
[mit]: https://github.com/cotes2020/chirpy-starter/blob/master/LICENSE
|
||||
[mit]: https://github.com/cotes2020/chirpy-starter/blob/master/LICENSE
|
||||
118
_config.yml
@ -1,38 +1,36 @@
|
||||
# The Site Configuration
|
||||
id: 'blog-dev'
|
||||
|
||||
# Import the theme
|
||||
theme: jekyll-theme-chirpy
|
||||
|
||||
# Change the following value to '/PROJECT_NAME' ONLY IF your site type is GitHub Pages Project sites
|
||||
# and doesn't have a custom domain.
|
||||
baseurl: ""
|
||||
|
||||
# The language of the webpage › http://www.lingoes.net/en/translator/langcode.htm
|
||||
# If it has the same name as one of the files in folder `_data/locales`, the layout language will also be changed,
|
||||
# otherwise, the layout language will use the default value of 'en'.
|
||||
lang: en
|
||||
|
||||
# Change to your timezone › http://www.timezoneconverter.com/cgi-bin/findzone/findzone
|
||||
# Change to your timezone › https://kevinnovak.github.io/Time-Zone-Picker
|
||||
timezone: Asia/Kolkata
|
||||
|
||||
# jekyll-seo-tag settings › https://github.com/jekyll/jekyll-seo-tag/blob/master/docs/usage.md
|
||||
# ↓ --------------------------
|
||||
|
||||
title: KBR Blogs # the main title
|
||||
title: Engineer's Odyssey # the main title
|
||||
|
||||
tagline: Only Blogging Nothing More or Less # it will display as the sub-title
|
||||
tagline: Your Hub for Tech, DIY, and Innovation # it will display as the subtitle
|
||||
|
||||
description: >- # used by seo meta and the atom feed
|
||||
Sharing knowledge through blogging using jekyll theme chirpy template.
|
||||
Through my articles and stories, I hope to guide you through the intricate world of technology, offer insights into automation's transformative power, unveil the secrets behind successful DIY projects, and encourage you to fearlessly pursue new skills.
|
||||
|
||||
# fill in the protocol & hostname for your site, e.g., 'https://username.github.io'
|
||||
url: "https://blogs.kottedi.in"
|
||||
# Fill in the protocol & hostname for your site.
|
||||
# E.g. 'https://username.github.io', note that it does not end with a '/'.
|
||||
url: "http://localhost:4000"
|
||||
|
||||
github:
|
||||
username: rajukottedi # change to your github username
|
||||
username: rajukottedi # change to your GitHub username
|
||||
|
||||
twitter:
|
||||
username: twitter_username # change to your twitter username
|
||||
username: RajuKottedi # change to your Twitter username
|
||||
|
||||
social:
|
||||
# Change to your full name.
|
||||
@ -41,19 +39,44 @@ social:
|
||||
email: bangararaju.kottedi@gmail.com # change to your email address
|
||||
links:
|
||||
# The first element serves as the copyright owner's link
|
||||
- https://twitter.com/rajukottedi # change to your twitter homepage
|
||||
- https://github.com/rajukottedi # change to your github homepage
|
||||
- https://github.com/rajukottedi # change to your GitHub homepage
|
||||
- https://twitter.com/rajukottedi # change to your Twitter homepage
|
||||
# Uncomment below to add more social links
|
||||
# - https://www.facebook.com/username
|
||||
# - https://www.linkedin.com/in/username
|
||||
- https://www.facebook.com/RajuKottedi435
|
||||
- https://www.linkedin.com/in/bangararajuk/
|
||||
|
||||
google_site_verification: # fill in to your verification string
|
||||
# Site Verification Settings
|
||||
webmaster_verifications:
|
||||
google: # fill in your Google verification code
|
||||
bing: # fill in your Bing verification code
|
||||
alexa: # fill in your Alexa verification code
|
||||
yandex: # fill in your Yandex verification code
|
||||
baidu: # fill in your Baidu verification code
|
||||
facebook: # fill in your Facebook verification code
|
||||
|
||||
# ↑ --------------------------
|
||||
# The end of `jekyll-seo-tag` settings
|
||||
|
||||
google_analytics:
|
||||
id: # fill in your Google Analytics ID
|
||||
# Web Analytics Settings
|
||||
analytics:
|
||||
google:
|
||||
id: # fill in your Google Analytics ID
|
||||
goatcounter:
|
||||
id: # fill in your GoatCounter ID
|
||||
umami:
|
||||
id: # fill in your Umami ID
|
||||
domain: # fill in your Umami domain
|
||||
matomo:
|
||||
id: # fill in your Matomo ID
|
||||
domain: # fill in your Matomo domain
|
||||
cloudflare:
|
||||
id: # fill in your Cloudflare Web Analytics token
|
||||
fathom:
|
||||
id: # fill in your Fathom Site ID
|
||||
|
||||
# Page Views settings
|
||||
pageviews:
|
||||
provider: # now only supports 'goatcounter'
|
||||
|
||||
# Prefer color scheme setting.
|
||||
#
|
||||
@ -66,24 +89,32 @@ google_analytics:
|
||||
# light - Use the light color scheme
|
||||
# dark - Use the dark color scheme
|
||||
#
|
||||
theme_mode: # [light|dark]
|
||||
theme_mode: # [light | dark]
|
||||
|
||||
# The CDN endpoint for images.
|
||||
# The CDN endpoint for media resources.
|
||||
# Notice that once it is assigned, the CDN url
|
||||
# will be added to all image (site avatar & posts' images) paths starting with '/'
|
||||
# will be added to all media resources (site avatar, posts' images, audio and video files) paths starting with '/'
|
||||
#
|
||||
# e.g. 'https://cdn.com'
|
||||
img_cdn:
|
||||
cdn:
|
||||
|
||||
# the avatar on sidebar, support local or CORS resources
|
||||
avatar: /assets/img/avatar.jpg
|
||||
|
||||
# The URL of the site-wide social preview image used in SEO `og:image` meta tag.
|
||||
# It can be overridden by a customized `page.image` in front matter.
|
||||
social_preview_image: # string, local or CORS resources
|
||||
|
||||
# boolean type, the global switch for TOC in posts.
|
||||
toc: true
|
||||
|
||||
comments:
|
||||
active: # The global switch for posts comments, e.g., 'disqus'. Keep it empty means disable
|
||||
# Global switch for the post comment system. Keeping it empty means disabled.
|
||||
provider: 'remark42' # [disqus | utterances | giscus]
|
||||
# The active options are as follows:
|
||||
# remark42 settings > https://remark42.com/
|
||||
remark42:
|
||||
host: 'https://remark42-uat.kottedi.in'
|
||||
disqus:
|
||||
shortname: # fill with the Disqus shortname. › https://help.disqus.com/en/articles/1717111-what-s-a-shortname
|
||||
# utterances settings › https://utteranc.es/
|
||||
@ -97,6 +128,7 @@ comments:
|
||||
category:
|
||||
category_id:
|
||||
mapping: # optional, default to 'pathname'
|
||||
strict: # optional, default to '0'
|
||||
input_position: # optional, default to 'bottom'
|
||||
lang: # optional, default to the value of `site.lang`
|
||||
reactions_enabled: # optional, default to the value of `1`
|
||||
@ -107,16 +139,28 @@ assets:
|
||||
enabled: # boolean, keep empty means false
|
||||
# specify the Jekyll environment, empty means both
|
||||
# only works if `assets.self_host.enabled` is 'true'
|
||||
env: # [development|production]
|
||||
env: # [development | production]
|
||||
|
||||
pwa:
|
||||
enabled: true # the option for PWA feature
|
||||
enabled: true # The option for PWA feature (installable)
|
||||
cache:
|
||||
enabled: true # the option for PWA offline cache
|
||||
# Paths defined here will be excluded from the PWA cache.
|
||||
# Usually its value is the `baseurl` of another website that
|
||||
# shares the same domain name as the current website.
|
||||
deny_paths:
|
||||
- "/api"
|
||||
# - "/example" # URLs match `<SITE_URL>/example/*` will not be cached by the PWA
|
||||
|
||||
paginate: 10
|
||||
|
||||
# The base URL of your site
|
||||
baseurl: "/blog"
|
||||
|
||||
# ------------ The following options are not recommended to be modified ------------------
|
||||
|
||||
kramdown:
|
||||
footnote_backlink: "↩︎"
|
||||
syntax_highlighter: rouge
|
||||
syntax_highlighter_opts: # Rouge Options › https://github.com/jneen/rouge#full-options
|
||||
css_class: highlight
|
||||
@ -153,10 +197,6 @@ defaults:
|
||||
values:
|
||||
layout: page
|
||||
permalink: /:title/
|
||||
- scope:
|
||||
path: assets/img/favicons
|
||||
values:
|
||||
swcache: true
|
||||
- scope:
|
||||
path: assets/js/dist
|
||||
values:
|
||||
@ -177,13 +217,15 @@ compress_html:
|
||||
exclude:
|
||||
- "*.gem"
|
||||
- "*.gemspec"
|
||||
- docs
|
||||
- tools
|
||||
- README.md
|
||||
- CHANGELOG.md
|
||||
- LICENSE
|
||||
- purgecss.js
|
||||
- rollup.config.js
|
||||
- node_modules
|
||||
- package*.json
|
||||
- "package*.json"
|
||||
- db_scripts
|
||||
- frontmatter.json
|
||||
|
||||
jekyll-archives:
|
||||
enabled: [categories, tags]
|
||||
@ -193,3 +235,13 @@ jekyll-archives:
|
||||
permalinks:
|
||||
tag: /tags/:name/
|
||||
category: /categories/:name/
|
||||
|
||||
show_drafts: true
|
||||
future: true
|
||||
|
||||
|
||||
# Custom configuration
|
||||
custom-config:
|
||||
blog-services:
|
||||
service_url: 'https://localhost:7013/blog/api/v1/posts'
|
||||
service_key: 'c6eAXYcNT873TT7BfMgQyS4ii7hxa53TLEUN7pAGaaU='
|
||||
@ -3,8 +3,12 @@
|
||||
- type: github
|
||||
icon: "fab fa-github"
|
||||
|
||||
- type: twitter
|
||||
icon: "fab fa-twitter"
|
||||
- type: linkedin
|
||||
icon: 'fab fa-linkedin' # icons powered by <https://fontawesome.com/>
|
||||
url: 'https://www.linkedin.com/in/bangararajuk/' # Fill with your Linkedin homepage
|
||||
|
||||
# - type: twitter
|
||||
# icon: "fa-brands fa-x-twitter"
|
||||
|
||||
- type: email
|
||||
icon: "fas fa-envelope"
|
||||
@ -19,10 +23,18 @@
|
||||
# icon: 'fab fa-mastodon' # icons powered by <https://fontawesome.com/>
|
||||
# url: '' # Fill with your Mastodon account page, rel="me" will be applied for verification
|
||||
#
|
||||
# - type: linkedin
|
||||
# icon: 'fab fa-linkedin' # icons powered by <https://fontawesome.com/>
|
||||
# url: '' # Fill with your Linkedin homepage
|
||||
#
|
||||
# - type: stack-overflow
|
||||
# icon: 'fab fa-stack-overflow'
|
||||
# url: '' # Fill with your stackoverflow homepage
|
||||
#
|
||||
# - type: bluesky
|
||||
# icon: 'fa-brands fa-bluesky'
|
||||
# url: '' # Fill with your Bluesky profile link
|
||||
#
|
||||
# - type: reddit
|
||||
# icon: 'fa-brands fa-reddit'
|
||||
# url: '' # Fill with your Reddit profile link
|
||||
#
|
||||
# - type: threads
|
||||
# icon: 'fa-brands fa-threads'
|
||||
# url: '' # Fill with your Threads profile link
|
||||
|
||||
94
_data/locales/en.yml
Normal file
@ -0,0 +1,94 @@
|
||||
# The layout text of site
|
||||
|
||||
# ----- Commons label -----
|
||||
|
||||
layout:
|
||||
post: Post
|
||||
category: Category
|
||||
tag: Tag
|
||||
|
||||
# The tabs of sidebar
|
||||
tabs:
|
||||
# format: <filename_without_extension>: <value>
|
||||
home: Home
|
||||
categories: Categories
|
||||
tags: Tags
|
||||
archives: Archives
|
||||
about: About
|
||||
|
||||
# the text displayed in the search bar & search results
|
||||
search:
|
||||
hint: search
|
||||
cancel: Cancel
|
||||
no_results: Oops! No results found.
|
||||
|
||||
panel:
|
||||
lastmod: Recently Updated
|
||||
trending_tags: Trending Tags
|
||||
toc: Contents
|
||||
|
||||
copyright:
|
||||
# Shown at the bottom of the post
|
||||
license:
|
||||
template: This post is licensed under :LICENSE_NAME by the author.
|
||||
name: CC BY 4.0
|
||||
link: https://creativecommons.org/licenses/by/4.0/
|
||||
|
||||
# Displayed in the footer
|
||||
brief: Some rights reserved.
|
||||
verbose: >-
|
||||
Except where otherwise noted, the blog posts on this site are licensed
|
||||
under the Creative Commons Attribution 4.0 International (CC BY 4.0) License by the author.
|
||||
|
||||
meta: Using the :THEME theme for :PLATFORM.
|
||||
|
||||
not_found:
|
||||
statment: Sorry, we've misplaced that URL or it's pointing to something that doesn't exist.
|
||||
|
||||
notification:
|
||||
update_found: A new version of content is available.
|
||||
update: Update
|
||||
|
||||
# ----- Posts related labels -----
|
||||
|
||||
post:
|
||||
written_by: By
|
||||
posted: Posted
|
||||
updated: Updated
|
||||
views: Views
|
||||
likes: Likes
|
||||
comments: Comments
|
||||
words: words
|
||||
pageview_measure: views
|
||||
read_time:
|
||||
unit: min
|
||||
prompt: read
|
||||
relate_posts: Further Reading
|
||||
share: Share
|
||||
button:
|
||||
next: Newer
|
||||
previous: Older
|
||||
copy_code:
|
||||
succeed: Copied!
|
||||
share_link:
|
||||
title: Copy link
|
||||
succeed: Link copied successfully!
|
||||
|
||||
# Date time format.
|
||||
# See: <http://strftime.net/>, <https://day.js.org/docs/en/display/format>
|
||||
df:
|
||||
post:
|
||||
strftime: "%b %e, %Y"
|
||||
dayjs: "ll"
|
||||
archives:
|
||||
strftime: "%b"
|
||||
dayjs: "MMM"
|
||||
|
||||
# categories page
|
||||
categories:
|
||||
category_measure:
|
||||
singular: category
|
||||
plural: categories
|
||||
post_measure:
|
||||
singular: post
|
||||
plural: posts
|
||||
@ -3,23 +3,48 @@
|
||||
|
||||
platforms:
|
||||
- type: Twitter
|
||||
icon: "fab fa-twitter"
|
||||
icon: "fa-brands fa-square-x-twitter"
|
||||
link: "https://twitter.com/intent/tweet?text=TITLE&url=URL"
|
||||
|
||||
- type: Facebook
|
||||
icon: "fab fa-facebook-square"
|
||||
link: "https://www.facebook.com/sharer/sharer.php?title=TITLE&u=URL"
|
||||
|
||||
- type: Telegram
|
||||
icon: "fab fa-telegram"
|
||||
link: "https://t.me/share/url?url=URL&text=TITLE"
|
||||
# - type: Telegram
|
||||
# icon: "fab fa-telegram"
|
||||
# link: "https://t.me/share/url?url=URL&text=TITLE"
|
||||
|
||||
# Uncomment below if you need to.
|
||||
#
|
||||
# - type: Linkedin
|
||||
# icon: "fab fa-linkedin"
|
||||
# link: "https://www.linkedin.com/sharing/share-offsite/?url=URL"
|
||||
- type: Linkedin
|
||||
icon: "fab fa-linkedin"
|
||||
link: "https://www.linkedin.com/sharing/share-offsite/?url=URL"
|
||||
#
|
||||
# - type: Weibo
|
||||
# icon: "fab fa-weibo"
|
||||
# link: "http://service.weibo.com/share/share.php?title=TITLE&url=URL"
|
||||
# link: "https://service.weibo.com/share/share.php?title=TITLE&url=URL"
|
||||
#
|
||||
# - type: Mastodon
|
||||
# icon: "fa-brands fa-mastodon"
|
||||
# # See: https://github.com/justinribeiro/share-to-mastodon#properties
|
||||
# instances:
|
||||
# - label: mastodon.social
|
||||
# link: "https://mastodon.social/"
|
||||
# - label: mastodon.online
|
||||
# link: "https://mastodon.online/"
|
||||
# - label: fosstodon.org
|
||||
# link: "https://fosstodon.org/"
|
||||
# - label: photog.social
|
||||
# link: "https://photog.social/"
|
||||
#
|
||||
# - type: Bluesky
|
||||
# icon: "fa-brands fa-bluesky"
|
||||
# link: "https://bsky.app/intent/compose?text=TITLE%20URL"
|
||||
#
|
||||
# - type: Reddit
|
||||
# icon: "fa-brands fa-square-reddit"
|
||||
# link: "https://www.reddit.com/submit?url=URL&title=TITLE"
|
||||
#
|
||||
# - type: Threads
|
||||
# icon: "fa-brands fa-square-threads"
|
||||
# link: "https://www.threads.net/intent/post?text=TITLE%20URL"
|
||||
8
_drafts/2024-07-08-anatomy-of-a-csrf-attack.md
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
title: Anatomy of a CSRF Attack
|
||||
date: 2024-07-08 0:00:00 +5:30
|
||||
tags:
|
||||
- csrf
|
||||
- security
|
||||
slug: anatomy-csrf-attack
|
||||
---
|
||||
28
_drafts/2025-02-02-getting-started-with-python.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
title: "Part 1: Getting Started with Python"
|
||||
description: Kick off your Python journey! Learn what Python is, set up your environment, and write your first program. Perfect for beginners—let’s start coding!
|
||||
date: 2025-02-02T16:59:46.763Z
|
||||
tags: []
|
||||
categories: []
|
||||
image:
|
||||
path: /assets/img/posts/python-getting-started.png
|
||||
disable-ty: false
|
||||
series: "Python Practical Guide: A Beginner's Journey"
|
||||
slug: part-1-started-python
|
||||
---
|
||||
|
||||
## What is Python?
|
||||
Python is a versatile, high-level programming language known for its simplicity and readability. It’s used for:
|
||||
- Web development
|
||||
- Data analysis and visualization
|
||||
- Artificial intelligence and machine learning
|
||||
- Automation and scripting
|
||||
- Game development
|
||||
- And much more!
|
||||
|
||||
## Why Learn Python?
|
||||
1. **Beginner-Friendly**: Easy-to-read syntax makes it perfect for first-time coders.
|
||||
2. **Versatile**: Use it for almost any type of project or industry.
|
||||
3. **In-Demand Skill**: Python developers are highly sought after in the job market.
|
||||
4. **Huge Community**: Tons of resources, libraries, and frameworks to help you learn and grow.
|
||||
5. **Fun and Powerful**: Automate tasks, analyze data, or build apps—Python makes it easy!
|
||||
@ -0,0 +1,12 @@
|
||||
---
|
||||
title: "Part 2: Python Basics - Syntax and Fundamentals"
|
||||
description: "Learn Python’s building blocks: variables, data types, input/output, and basic operations. Start writing clean, readable code today!"
|
||||
date: 2025-02-02T18:04:14.853Z
|
||||
tags: []
|
||||
categories: []
|
||||
image:
|
||||
path: /assets/img/posts/python-logo.jpg
|
||||
disable-ty: true
|
||||
series: "Python Practical Guide: A Beginner's Journey"
|
||||
slug: part-2-python-basics-syntax-fundamentals
|
||||
---
|
||||
5
_includes/comment.html
Normal file
@ -0,0 +1,5 @@
|
||||
<!-- The comments switcher -->
|
||||
{% if page.comments and site.comments.provider %}
|
||||
{% capture path %}comments/{{ site.comments.provider }}.html{% endcapture %}
|
||||
{% include {{ path }} %}
|
||||
{% endif %}
|
||||
33
_includes/comments-counter.html
Normal file
@ -0,0 +1,33 @@
|
||||
<style>
|
||||
.fa-comment {
|
||||
margin-left: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i class="fa fa-comment" aria-hidden="true" {% if include.tooltip %}
|
||||
title="{{site.data.locales[include.lang].post.comments}}" data-bs-toggle="tooltip" data-bs-placement="bottom" {%
|
||||
endif %}></i>
|
||||
<span class="remark42__counter"></span>
|
||||
|
||||
<script>
|
||||
function commentTextChange() {
|
||||
var target = document.querySelector('.remark42__counter');
|
||||
if(!target){
|
||||
window.setTimeout(commentTextChange, 500);
|
||||
return;
|
||||
}
|
||||
var observer = new MutationObserver(function (mutations) {
|
||||
const comments = document.querySelector(".remark42__counter");
|
||||
var commentsCount = 0;
|
||||
if(Number(comments.innerText) !== NaN){
|
||||
commentsCount = Number(comments.innerText);
|
||||
}
|
||||
if(commentsCount !== commentsCountFromResponse){
|
||||
ajax(serviceUrl + "/UpdatePostCommentsCount?blogUrl=" + siteId + "&postSlug=" + postId, "POST", commentsCount);
|
||||
}
|
||||
});
|
||||
var config = { childList: true };
|
||||
observer.observe(target, config);
|
||||
}
|
||||
commentTextChange();
|
||||
</script>
|
||||
33
_includes/comments/remark42.html
Normal file
@ -0,0 +1,33 @@
|
||||
<div id="remark42">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var remark_config = {
|
||||
host: remark42Host,
|
||||
site_id: "{{site.id}}",
|
||||
components: ['embed', 'counter'],
|
||||
|
||||
max_shown_comments: 10,
|
||||
theme: Theme.visualState,
|
||||
|
||||
locale: 'en',
|
||||
show_email_subscription: false
|
||||
};
|
||||
|
||||
(function (c) {
|
||||
for (var i = 0; i < c.length; i++) {
|
||||
var d = document, s = d.createElement('script');
|
||||
s.src = remark_config.host + '/web/' + c[i] + '.js';
|
||||
s.defer = true;
|
||||
(d.head || d.body).appendChild(s);
|
||||
}
|
||||
})(remark_config.components || ['embed']);
|
||||
|
||||
var targetDiv = document.getElementById("mode-toggle");
|
||||
|
||||
if (targetDiv != null) {
|
||||
targetDiv.addEventListener("click", () => {
|
||||
window.REMARK42.changeTheme(Theme.visualState === 'light' ? 'dark' : 'light');
|
||||
})
|
||||
}
|
||||
</script>
|
||||
196
_includes/head.html
Normal file
@ -0,0 +1,196 @@
|
||||
<!-- The Head -->
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#f7f7f7">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#1b1b1e">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no initial-scale=1, shrink-to-fit=no, viewport-fit=cover">
|
||||
|
||||
{%- capture seo_tags -%}
|
||||
{% seo title=false %}
|
||||
{%- endcapture -%}
|
||||
|
||||
<!-- Setup Open Graph image -->
|
||||
|
||||
{% if page.image %}
|
||||
{% assign src = page.image.path | default: page.image %}
|
||||
|
||||
{% unless src contains '://' %}
|
||||
{%- capture img_url -%}
|
||||
{% include media-url.html src=src subpath=page.media_subpath absolute=true %}
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- capture old_url -%}{{ src | absolute_url }}{%- endcapture -%}
|
||||
{%- capture new_url -%}{{ img_url }}{%- endcapture -%}
|
||||
|
||||
{% assign seo_tags = seo_tags | replace: old_url, new_url %}
|
||||
{% endunless %}
|
||||
|
||||
{% elsif site.social_preview_image %}
|
||||
{%- capture img_url -%}
|
||||
{% include media-url.html src=site.social_preview_image absolute=true %}
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- capture og_image -%}
|
||||
<meta property="og:image" content="{{ img_url }}" />
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- capture twitter_image -%}
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:image" content="{{ img_url }}" />
|
||||
{%- endcapture -%}
|
||||
|
||||
{% assign old_meta_clip = '<meta name="twitter:card" content="summary" />' %}
|
||||
{% assign new_meta_clip = og_image | append: twitter_image %}
|
||||
{% assign seo_tags = seo_tags | replace: old_meta_clip, new_meta_clip %}
|
||||
{% endif %}
|
||||
|
||||
{{ seo_tags }}
|
||||
|
||||
<title>
|
||||
{%- unless page.layout == 'home' -%}
|
||||
{{ page.title | append: ' | ' }}
|
||||
{%- endunless -%}
|
||||
{{ site.title }}
|
||||
</title>
|
||||
|
||||
{% include_cached favicons.html %}
|
||||
|
||||
<!-- Resource Hints -->
|
||||
{% unless site.assets.self_host.enabled %}
|
||||
{% for hint in site.data.origin.cors.resource_hints %}
|
||||
{% for link in hint.links %}
|
||||
<link rel="{{ link.rel }}" href="{{ hint.url }}" {{ link.opts | join: ' ' }}>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endunless %}
|
||||
|
||||
<!-- Bootstrap -->
|
||||
{% unless jekyll.environment == 'production' %}
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
|
||||
{% endunless %}
|
||||
|
||||
<!-- Theme style -->
|
||||
<link rel="stylesheet" href="{{ '/assets/css/:THEME.css' | replace: ':THEME', site.theme | relative_url }}">
|
||||
|
||||
<!-- Web Font -->
|
||||
<link rel="stylesheet" href="{{ site.data.origin[type].webfonts | relative_url }}">
|
||||
|
||||
<!-- Font Awesome Icons -->
|
||||
<link rel="stylesheet" href="{{ site.data.origin[type].fontawesome.css | relative_url }}">
|
||||
|
||||
<!-- 3rd-party Dependencies -->
|
||||
|
||||
{% if site.toc and page.toc %}
|
||||
<link rel="stylesheet" href="{{ site.data.origin[type].toc.css | relative_url }}">
|
||||
{% endif %}
|
||||
|
||||
{% if page.layout == 'post' or page.layout == 'page' or page.layout == 'home' %}
|
||||
<link rel="stylesheet" href="{{ site.data.origin[type]['lazy-polyfill'].css | relative_url }}">
|
||||
{% endif %}
|
||||
|
||||
{% if page.layout == 'page' or page.layout == 'post' %}
|
||||
<!-- Image Popup -->
|
||||
<link rel="stylesheet" href="{{ site.data.origin[type].glightbox.css | relative_url }}">
|
||||
{% endif %}
|
||||
|
||||
<!-- Scripts -->
|
||||
<script>
|
||||
var serviceUrl = "{{site.custom-config.blog-services.service_url}}";
|
||||
var serviceApiKey = "{{site.custom-config.blog-services.service_key}}";
|
||||
var siteId = "{{site.url}}{{site.baseurl}}";
|
||||
var postId = "{{page.slug}}";
|
||||
var remark42Host = "{{ site.comments.remark42.host }}";
|
||||
|
||||
var postDetails = {
|
||||
postUrl: "{{site.url}}{{site.baseurl}}{{page.url}}",
|
||||
slug: "{{page.slug}}",
|
||||
title: "{{page.title}}",
|
||||
description: "{{page.description}}",
|
||||
categories: [],
|
||||
image: "{{page.image.path}}",
|
||||
createdDate: "{{page.date}}",
|
||||
modifiedDate: "{{page.last_modified_at | strip}}",
|
||||
blogUrl: "{{site.url}}{{site.baseurl}}",
|
||||
views: 0,
|
||||
likes: 0,
|
||||
comments: 0
|
||||
};
|
||||
|
||||
function liquidArrayToJsonArray(){
|
||||
var items = [];
|
||||
{% for category in page.categories %}
|
||||
{% assign cat = category %}
|
||||
items.push("{{cat}}");
|
||||
{% endfor %}
|
||||
return items;
|
||||
}
|
||||
|
||||
postDetails.categories = liquidArrayToJsonArray();
|
||||
|
||||
function ajax(url, type, payload) {
|
||||
if (payload === undefined) {
|
||||
payload = null;
|
||||
}
|
||||
return new Promise(function (resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onload = function () {
|
||||
resolve(this.responseText);
|
||||
};
|
||||
xhr.onerror = reject;
|
||||
xhr.open(type, url);
|
||||
xhr.setRequestHeader('XApiKey', serviceApiKey);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.setRequestHeader('ngsw-bypass', '');
|
||||
xhr.send(JSON.stringify(payload));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% if jekyll.environment == 'production' and site.google_analytics.id != empty and site.google_analytics.id %}
|
||||
<!-- Google Tag Manager -->
|
||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-W4PKSB86');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
|
||||
<!-- Microsoft Clarity -->
|
||||
<script type="text/javascript">
|
||||
(function(c,l,a,r,i,t,y){
|
||||
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
|
||||
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
|
||||
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
||||
})(window, document, "clarity", "script", "ils01ejdug");
|
||||
</script>
|
||||
<!-- End Microsoft Clarity -->
|
||||
{% endif %}
|
||||
|
||||
<script src="{{ '/assets/js/dist/theme.min.js' | relative_url }}"></script>
|
||||
|
||||
{% include js-selector.html lang=lang %}
|
||||
|
||||
{% if jekyll.environment == 'production' %}
|
||||
<!-- PWA -->
|
||||
{% if site.pwa.enabled %}
|
||||
<script
|
||||
defer
|
||||
src="{{ '/app.min.js' | relative_url }}?baseurl={{ site.baseurl | default: '' }}®ister={{ site.pwa.cache.enabled }}"
|
||||
></script>
|
||||
{% endif %}
|
||||
|
||||
<!-- Web Analytics -->
|
||||
{% for analytics in site.analytics %}
|
||||
{% capture str %}{{ analytics }}{% endcapture %}
|
||||
{% assign platform = str | split: '{' | first %}
|
||||
{% if site.analytics[platform].id and site.analytics[platform].id != empty %}
|
||||
{% include analytics/{{ platform }}.html %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% include metadata-hook.html %}
|
||||
</head>
|
||||
71
_includes/likes-views.html
Normal file
@ -0,0 +1,71 @@
|
||||
<style>
|
||||
.heart-icon {
|
||||
margin-bottom: -41px;
|
||||
margin-right: -39.5px;
|
||||
display: inline-flex;
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
transform: translate(-50%, -50%);
|
||||
background: url("{{ '/assets/img/custom/heart.png' | relative_url }}") no-repeat;
|
||||
background-position: 0 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.heart-icon.liked {
|
||||
animation: like-anim 0.7s steps(28) forwards;
|
||||
}
|
||||
|
||||
@keyframes like-anim {
|
||||
to {
|
||||
background-position: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
{% include views.html tooltip=true onload=false lang=lang %}
|
||||
{% include likes.html tooltip=true onload=false lang=lang %}
|
||||
|
||||
<script>
|
||||
const views = document.getElementById("views-count");
|
||||
const likes = document.getElementById("likes-count");
|
||||
const postLikesAndViews = document.getElementById("likes-views");
|
||||
var commentsCountFromResponse = 0;
|
||||
|
||||
postLikesAndViews.style.display = "none";
|
||||
|
||||
ajax(serviceUrl + "/GetPostLikesAndViews?blogUrl=" + siteId + "&postSlug=" + postId, "GET")
|
||||
.then(function (result) {
|
||||
if (result != "N/A") {
|
||||
var response = JSON.parse(result);
|
||||
if(!response.postExists){
|
||||
postDetails.likes = postDetails.views = postDetails.comments = 0;
|
||||
if(Number(likes.innerText) !== NaN){
|
||||
postDetails.likes = Number(likes.innerText);
|
||||
}
|
||||
if(Number(views.innerText) !== NaN){
|
||||
postDetails.views = Number(views.innerText);
|
||||
}
|
||||
if(postDetails.modifiedDate === ""){
|
||||
postDetails.modifiedDate = postDetails.createdDate;
|
||||
}
|
||||
}
|
||||
|
||||
ajax(serviceUrl + "/CreatePost", "POST", postDetails)
|
||||
.then(function (result) {
|
||||
if(result != "N/A"){
|
||||
var response = JSON.parse(result);
|
||||
setPostMetrics(response);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function setPostMetrics(response){
|
||||
if(response.postExists){
|
||||
views.innerText = response.views;
|
||||
likes.innerText = response.likes;
|
||||
commentsCountFromResponse = response.comments;
|
||||
postLikesAndViews.style.display = "inline";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
65
_includes/likes.html
Normal file
@ -0,0 +1,65 @@
|
||||
<style>
|
||||
.heart-icon {
|
||||
margin-bottom: -41px;
|
||||
margin-right: -39.5px;
|
||||
display: inline-flex;
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
transform: translate(-50%, -50%);
|
||||
background: url("{{ '/assets/img/custom/heart.png' | relative_url }}") no-repeat;
|
||||
background-position: 0 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.heart-icon.liked {
|
||||
animation: like-anim 0.7s steps(28) forwards;
|
||||
}
|
||||
|
||||
@keyframes like-anim {
|
||||
to {
|
||||
background-position: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<i class="heart-icon" aria-hidden="true" {% if include.tooltip %} title="{{site.data.locales[include.lang].post.likes}}"
|
||||
data-bs-toggle="tooltip" data-bs-placement="bottom" {% endif %}></i>
|
||||
<em id="likes-count"></em>
|
||||
|
||||
<script>
|
||||
const heartIcon = document.querySelector(".heart-icon");
|
||||
const likesCount = document.getElementById("likes-count");
|
||||
const likesOnLoad = "true";
|
||||
|
||||
function onLoadAndClick() {
|
||||
heartIcon.style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
function apiCall(url, event) {
|
||||
onLoadAndClick();
|
||||
ajax(url, "POST")
|
||||
.then(function (result) {
|
||||
if (event === "click") {
|
||||
heartIcon.classList.toggle("liked");
|
||||
}
|
||||
likesCount.innerHTML = result;
|
||||
heartIcon.style.pointerEvents = 'auto';
|
||||
});
|
||||
}
|
||||
|
||||
var apiUrl = serviceUrl + "/GetPostLikes?blogUrl=" + siteId + "&postSlug=" + postId;
|
||||
|
||||
if(likesOnLoad == "{{include.onload}}"){
|
||||
apiCall(apiUrl);
|
||||
}
|
||||
|
||||
heartIcon.addEventListener("click", () => {
|
||||
if (!heartIcon.classList.contains("liked")) {
|
||||
apiUrl = serviceUrl + "/LikePost?blogUrl=" + siteId + "&postSlug=" + postId;
|
||||
} else {
|
||||
apiUrl = serviceUrl + "/DislikePost?blogUrl=" + siteId + "&postSlug=" + postId;
|
||||
}
|
||||
|
||||
apiCall(apiUrl, "click");
|
||||
})
|
||||
</script>
|
||||
1
_includes/loader.html
Normal file
@ -0,0 +1 @@
|
||||
<i id="{{include.id}}" class="fa-solid fa-circle-notch fa-spin" {% if include.style %} style="{{include.style}}" {% endif %}></i>
|
||||
28
_includes/post-series.html
Normal file
@ -0,0 +1,28 @@
|
||||
<style>
|
||||
#post-series {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% if page.series %}
|
||||
{% assign posts = site.posts | where: "series", page.series | sort: 'date' %}
|
||||
<div id="post-series">
|
||||
<div class="card">
|
||||
<div>
|
||||
<h3>{{ page.series }} ({{ posts | size }} - Part Series)</h3>
|
||||
<ol>
|
||||
{% assign posts = site.posts | where: "series", page.series | sort: "date" %}
|
||||
{% for post in posts %}
|
||||
<li>{% if post.url == page.url %}{{ post.title }} {% else %}
|
||||
<a href="{{ site.baseurl }}{{ post.url }}">{{ post.title }}</a>{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
47
_includes/thank-you.html
Normal file
@ -0,0 +1,47 @@
|
||||
{% capture title %}{{ page.title }} - {{ site.title }}{% endcapture %}
|
||||
{% assign title = title | uri_escape %}
|
||||
{% assign url = page.url | absolute_url | url_encode %}
|
||||
{% assign link = site.data.share.platforms[2].link | replace: 'TITLE', title | replace: 'URL', url %}
|
||||
<style>
|
||||
.thank-you {
|
||||
border-left: 5px solid #959595;
|
||||
margin: 1.5em 0;
|
||||
padding: 0.5em 1em;
|
||||
color: inherit;
|
||||
font-style: italic;
|
||||
margin-left: -20px;
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Thank you message -->
|
||||
<blockquote class="thank-you">
|
||||
<p>Thank you for reading. I trust it provided value to you. Your feedback
|
||||
is important, so please share your thoughts in the <a href="#remark42"><strong>comments</strong></a> below. If
|
||||
you found it helpful, consider giving it a <a href="#topbar-wrapper" id="ty-like"><strong>like</strong></a> and
|
||||
<a href="{{ link }}" target="_blank" rel="noopener" data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
aria-label="Linkedin" data-bs-original-title="Linkedin" aria-describedby="tooltip190113">
|
||||
<strong>sharing</strong></a> it with others, also feel free to check out my [LinkedIn profile] - <a
|
||||
href="{{ site.data.contact[1].url }}" target="_blank"><strong>Bangara Raju Kottedi</strong></a>.
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
<script>
|
||||
|
||||
function handleDelayedClick() {
|
||||
setTimeout(function() {
|
||||
heartIcon.click();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const tyLike = document.getElementById("ty-like");
|
||||
|
||||
tyLike.addEventListener("click", (event) => {
|
||||
if (!heartIcon.classList.contains("liked")) {
|
||||
handleDelayedClick();
|
||||
}
|
||||
else{
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
18
_includes/views.html
Normal file
@ -0,0 +1,18 @@
|
||||
<i class="fa fa-eye fa-fw" aria-hidden="true" {% if include.tooltip %} title="{{site.data.locales[include.lang].post.views}}"
|
||||
data-bs-toggle="tooltip" data-bs-placement="bottom" {% endif %}></i>
|
||||
<em id="views-count" style="margin-right: 12px"></em>
|
||||
|
||||
<script>
|
||||
const viewsOnLoad = "true";
|
||||
if(viewsOnLoad == "{{include.onload}}"){
|
||||
const viewsCount = document.getElementById("views-count");
|
||||
const viewsLoader = document.getElementById("views-loader");
|
||||
|
||||
ajax(serviceUrl + "/GetPostViews?siteId=" + siteId + "&postId=" + postId)
|
||||
.then(function (result) {
|
||||
viewsCount.innerText = result;
|
||||
})
|
||||
.catch(function () {
|
||||
});
|
||||
}
|
||||
</script>
|
||||
94
_layouts/default.html
Normal file
@ -0,0 +1,94 @@
|
||||
---
|
||||
layout: compress
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
|
||||
{% include origin-type.html %}
|
||||
|
||||
{% include lang.html %}
|
||||
{% if site.theme_mode %}
|
||||
{% capture prefer_mode %}data-mode="{{ site.theme_mode }}"{% endcapture %}
|
||||
{% endif %}
|
||||
|
||||
<!-- `site.alt_lang` can specify a language different from the UI -->
|
||||
<html lang="{{ page.lang | default: site.lang }}" {{ prefer_mode }}>
|
||||
{% include head.html %}
|
||||
|
||||
<body>
|
||||
|
||||
{% if jekyll.environment == 'production' and site.google_analytics.id != empty and site.google_analytics.id %}
|
||||
<!-- Google Tag Manager (noscript) -->
|
||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-W4PKSB86" height="0" width="0"
|
||||
style="display:none;visibility:hidden"></iframe></noscript>
|
||||
<!-- End Google Tag Manager (noscript) -->
|
||||
{% endif %}
|
||||
|
||||
{% include sidebar.html lang=lang %}
|
||||
|
||||
<div id="main-wrapper" class="d-flex justify-content-center">
|
||||
<div class="container d-flex flex-column px-xxl-5">
|
||||
{% include topbar.html lang=lang %}
|
||||
|
||||
<div class="row flex-grow-1">
|
||||
<main aria-label="Main Content" class="col-12 col-lg-11 col-xl-9 px-md-4">
|
||||
{% if layout.refactor or layout.layout == 'default' %}
|
||||
{% include refactor-content.html content=content lang=lang %}
|
||||
{% else %}
|
||||
{{ content }}
|
||||
{% endif %}
|
||||
</main>
|
||||
|
||||
<!-- panel -->
|
||||
<aside aria-label="Panel" id="panel-wrapper" class="col-xl-3 ps-2 text-muted">
|
||||
<div class="access">
|
||||
{% include_cached update-list.html lang=lang %}
|
||||
{% include_cached trending-tags.html lang=lang %}
|
||||
</div>
|
||||
|
||||
{% for _include in layout.panel_includes %}
|
||||
{% assign _include_path = _include | append: '.html' %}
|
||||
{% include {{ _include_path }} lang=lang %}
|
||||
{% endfor %}
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- tail -->
|
||||
<div id="tail-wrapper" class="col-12 col-lg-11 col-xl-9 px-md-4">
|
||||
{% for _include in layout.tail_includes %}
|
||||
{% assign _include_path = _include | append: '.html' %}
|
||||
{% include {{ _include_path }} lang=lang %}
|
||||
{% endfor %}
|
||||
|
||||
{% include_cached footer.html lang=lang %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include_cached search-results.html lang=lang %}
|
||||
</div>
|
||||
|
||||
<aside aria-label="Scroll to Top">
|
||||
<button id="back-to-top" type="button" class="btn btn-lg btn-box-shadow">
|
||||
<i class="fas fa-angle-up"></i>
|
||||
</button>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div id="mask" class="d-none position-fixed w-100 h-100 z-1"></div>
|
||||
|
||||
{% if site.pwa.enabled %}
|
||||
{% include_cached notification.html lang=lang %}
|
||||
{% endif %}
|
||||
|
||||
<!-- Embedded scripts -->
|
||||
|
||||
{% for _include in layout.script_includes %}
|
||||
{% assign _include_path = _include | append: '.html' %}
|
||||
{% include {{ _include_path }} %}
|
||||
{% endfor %}
|
||||
|
||||
{% include_cached search-loader.html lang=lang %}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
190
_layouts/post.html
Normal file
@ -0,0 +1,190 @@
|
||||
---
|
||||
layout: default
|
||||
refactor: true
|
||||
panel_includes:
|
||||
- toc
|
||||
tail_includes:
|
||||
- related-posts
|
||||
- post-nav
|
||||
- comment
|
||||
script_includes:
|
||||
- comment
|
||||
---
|
||||
|
||||
{% include lang.html %}
|
||||
|
||||
{% include toc-status.html %}
|
||||
|
||||
<article class="px-1" data-toc="{{ enable_toc }}">
|
||||
<header>
|
||||
<h1 data-toc-skip>{{ page.title }}</h1>
|
||||
{% if page.description %}
|
||||
<p class="post-desc fw-light mb-4">{{ page.description }}</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="post-meta text-muted">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<!-- published date -->
|
||||
<span>
|
||||
{{ site.data.locales[lang].post.posted }}
|
||||
{% include datetime.html date=page.date tooltip=true lang=lang %}
|
||||
</span>
|
||||
|
||||
<!-- lastmod date -->
|
||||
{% if page.last_modified_at and page.last_modified_at != page.date %}
|
||||
<span>
|
||||
{{ site.data.locales[lang].post.updated }}
|
||||
{% include datetime.html date=page.last_modified_at tooltip=true lang=lang %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- post counter -->
|
||||
<div id="likes-views">
|
||||
{% include likes-views.html tooltip=true lang=lang %}
|
||||
{% include comments-counter.html tooltip=true lang=lang %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% if page.image %}
|
||||
{% capture src %}src="{{ page.image.path | default: page.image }}"{% endcapture %}
|
||||
{% capture class %}class="preview-img{% if page.image.no_bg %}{{ ' no-bg' }}{% endif %}"{% endcapture %}
|
||||
{% capture alt %}alt="{{ page.image.alt | xml_escape | default: "Preview Image" }}"{% endcapture %}
|
||||
|
||||
{% if page.image.lqip %}
|
||||
{%- capture lqip -%}lqip="{{ page.image.lqip }}"{%- endcapture -%}
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-3 mb-3">
|
||||
<img {{ src }} {{ class }} {{ alt }} w="1200" h="630" {{ lqip }}>
|
||||
{%- if page.image.alt -%}
|
||||
<figcaption class="text-center pt-2 pb-2">{{ page.image.alt }}</figcaption>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<!-- author(s) -->
|
||||
<span>
|
||||
{% if page.author %}
|
||||
{% assign authors = page.author %}
|
||||
{% elsif page.authors %}
|
||||
{% assign authors = page.authors %}
|
||||
{% endif %}
|
||||
|
||||
{{ site.data.locales[lang].post.written_by }}
|
||||
|
||||
<em>
|
||||
{% if authors %}
|
||||
{% for author in authors %}
|
||||
{% if site.data.authors[author].url -%}
|
||||
<a href="{{ site.data.authors[author].url }}">{{ site.data.authors[author].name }}</a>
|
||||
{%- else -%}
|
||||
{{ site.data.authors[author].name }}
|
||||
{%- endif %}
|
||||
{% unless forloop.last %}{{ '</em>, <em>' }}{% endunless %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<a href="{{ site.social.links[0] }}">{{ site.social.name }}</a>
|
||||
{% endif %}
|
||||
</em>
|
||||
</span>
|
||||
|
||||
<div>
|
||||
<!-- pageviews -->
|
||||
{% if site.pageviews.provider and site.analytics[site.pageviews.provider].id %}
|
||||
<span>
|
||||
<em id="pageviews">
|
||||
<i class="fas fa-spinner fa-spin small"></i>
|
||||
</em>
|
||||
{{ site.data.locales[lang].post.pageview_measure }}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
<!-- read time -->
|
||||
{% include read-time.html content=content prompt=true lang=lang %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- .post-meta -->
|
||||
{% include post-series.html %}
|
||||
</header>
|
||||
|
||||
{% if enable_toc %}
|
||||
<div id="toc-bar" class="d-flex align-items-center justify-content-between invisible">
|
||||
<span class="label text-truncate">{{ page.title }}</span>
|
||||
<button type="button" class="toc-trigger btn me-1">
|
||||
<i class="fa-solid fa-list-ul fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button id="toc-solo-trigger" type="button" class="toc-trigger btn btn-outline-secondary btn-sm">
|
||||
<span class="label ps-2 pe-1">{{- site.data.locales[lang].panel.toc -}}</span>
|
||||
<i class="fa-solid fa-angle-right fa-fw"></i>
|
||||
</button>
|
||||
|
||||
<dialog id="toc-popup" class="p-0">
|
||||
<div class="header d-flex flex-row align-items-center justify-content-between">
|
||||
<div class="label text-truncate py-2 ms-4">{{- page.title -}}</div>
|
||||
<button id="toc-popup-close" type="button" class="btn mx-1 my-1 opacity-75">
|
||||
<i class="fas fa-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="toc-popup-content" class="px-4 py-3 pb-4"></div>
|
||||
</dialog>
|
||||
{% endif %}
|
||||
|
||||
<div class="content">
|
||||
{{ content }}
|
||||
{% if page.disable-ty != true %}
|
||||
{% include thank-you.html %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="post-tail-wrapper text-muted">
|
||||
<!-- categories -->
|
||||
{% if page.categories.size > 0 %}
|
||||
<div class="post-meta mb-3">
|
||||
<i class="far fa-folder-open fa-fw me-1"></i>
|
||||
{% for category in page.categories %}
|
||||
<a href="{{ site.baseurl }}/categories/{{ category | slugify | url_encode }}/">{{ category }}</a>
|
||||
{%- unless forloop.last -%},{%- endunless -%}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- tags -->
|
||||
{% if page.tags.size > 0 %}
|
||||
<div class="post-tags">
|
||||
<i class="fa fa-tags fa-fw me-1"></i>
|
||||
{% for tag in page.tags %}
|
||||
<a href="{{ site.baseurl }}/tags/{{ tag | slugify | url_encode }}/" class="post-tag no-text-decoration">
|
||||
{{- tag -}}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="
|
||||
post-tail-bottom
|
||||
d-flex justify-content-between align-items-center mt-5 pb-2
|
||||
">
|
||||
<div class="license-wrapper">
|
||||
{% if site.data.locales[lang].copyright.license.template %}
|
||||
{% capture _replacement %}
|
||||
<a href="{{ site.data.locales[lang].copyright.license.link }}">
|
||||
{{ site.data.locales[lang].copyright.license.name }}
|
||||
</a>
|
||||
{% endcapture %}
|
||||
|
||||
{{ site.data.locales[lang].copyright.license.template | replace: ':LICENSE_NAME', _replacement }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% include post-sharing.html lang=lang %}
|
||||
</div>
|
||||
<!-- .post-tail-bottom -->
|
||||
</div>
|
||||
<!-- div.post-tail-wrapper -->
|
||||
</article>
|
||||
@ -1,3 +1,14 @@
|
||||
# Hello World!
|
||||
---
|
||||
title: Hello World
|
||||
date: 2023-07-22 0:00:00 +0530
|
||||
tags:
|
||||
- hello world
|
||||
slug: hello-world
|
||||
description: Hello World
|
||||
categories: []
|
||||
disable-ty: true
|
||||
image:
|
||||
path: /assets/img/posts/hello-world.jpg
|
||||
---
|
||||
|
||||
## Welcome to my blog!
|
||||
## Hello World
|
||||
|
||||
48
_posts/2023-07-22-self-hosted-commenting-system.md
Normal file
@ -0,0 +1,48 @@
|
||||
---
|
||||
title: Self-Hosted Commenting System
|
||||
date: 2023-07-23 0:00:00 +0530
|
||||
tags:
|
||||
- privacy
|
||||
- self-hosted
|
||||
- comment-system
|
||||
- remark42
|
||||
categories:
|
||||
- Open Source
|
||||
- Services
|
||||
slug: hosted-commenting-system
|
||||
image:
|
||||
path: /assets/img/posts/remark42.png
|
||||
keywords:
|
||||
- remark42
|
||||
- self-hosted
|
||||
- privacy
|
||||
---
|
||||
|
||||
## Privacy-focused lightweight commenting engine
|
||||
|
||||
## [Remark 42](https://remark42.com/)
|
||||
|
||||
### It is a self-hosted, lightweight, and simple (yet functional) commenting system, which doesn’t spy on users. It can be embedded into blogs, articles or any other place where readers add comments.
|
||||
|
||||
### Features
|
||||
|
||||
1. Social login via Google, Facebook, Github, and Yandex
|
||||
2. Optional anonymous access
|
||||
3. Multi-level nested comments with both tree and plain presentations
|
||||
3. Import from Disqus and WordPress
|
||||
3. Markdown support with friendly formatter toolbar
|
||||
3. Moderator can remove comments and block users
|
||||
3. Voting, pinning and verification system
|
||||
3. Sortable comments
|
||||
3. Images upload with drag-and-drop
|
||||
3. Extractor for recent comments, cross-post
|
||||
3. RSS for all comments and each post
|
||||
3. Telegram notifications
|
||||
3. Export data to JSON with automatic backups
|
||||
3. No external databases, everything embedded in a single data file
|
||||
3. Fully dockerized and can be deployed in a single command
|
||||
3. The self-contained executable can be deployed directly to Linux, Windows, and MacOS
|
||||
3. Clean, lightweight and customizable UI with white and dark themes
|
||||
3. Multi-site mode from a single instance
|
||||
3. Integration with automatic SSL (direct and via NGINX-le)
|
||||
3. Privacy-focused
|
||||
213
_posts/2024-04-03-solid-principles.md
Normal file
@ -0,0 +1,213 @@
|
||||
---
|
||||
title: SOLID Principles
|
||||
description: The SOLID principles are a set of design principles aimed at improving software quality, making it easier to understand, extend, and maintain.
|
||||
date: 2024-04-03T16:10:57.476Z
|
||||
tags:
|
||||
- code quality
|
||||
- oops
|
||||
- principles
|
||||
categories:
|
||||
- Programming
|
||||
- Principles
|
||||
image:
|
||||
path: /assets/img/posts/solid-principles.jpg
|
||||
alt: Understanding SOLID Principles
|
||||
slug: solid-principles
|
||||
---
|
||||
|
||||
The SOLID principles are a set of **five design principles** that are intended to guide software development to create more **understandable**, **maintainable**, **extendable** and **scalable** code. These principles were introduced by **Robert C. Martin** (also known as Uncle Bob) in the early 2000s and have since become fundamental concepts in object-oriented design and programming. Here's a brief overview of each principle:
|
||||
|
||||
## 1. Single Responsibility Principle (SRP):
|
||||
This principle states that a class should have **only one reason to change**. In other words, a class should have **only one responsibility or job**. By adhering to SRP, you ensure that classes are focused and have clear, understandable purposes, which makes them **easier to maintain and test**.
|
||||
|
||||
**Example**: *Think of a chef in a restaurant. Instead of having a chef who both cooks meals and serves customers, you'd want separate roles. The chef should focus on cooking delicious dishes, while a waiter takes care of serving customers.*
|
||||
|
||||
```c#
|
||||
// Before
|
||||
public class Chef
|
||||
{
|
||||
public void CookMeals() { /*...*/ }
|
||||
public void ServeMeals() { /*...*/ }
|
||||
}
|
||||
|
||||
// After
|
||||
public class Chef
|
||||
{
|
||||
public void CookMeals() { /*...*/ }
|
||||
}
|
||||
|
||||
public class Waiter
|
||||
{
|
||||
public void ServeMeals() { /*...*/ }
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 2. Open/Closed Principle (OCP):
|
||||
This Principle suggests that software entities `(classes, modules, functions, etc.)` should be **open for extension** but **closed for modification**. This means that you should be able to **extend the behavior of a module without modifying its source code**. This is typically achieved through the use of **inheritance**, **polymorphism** and **parameters**.
|
||||
|
||||
**Example**: *Consider a shape drawing application. Instead of modifying the existing shape classes every time you need to add a new shape, you'd create a abstact class called **Shape** and implement it in different shape classes like **Circle**, **Square**, etc. Then, when you want to add a new shape, you create a new class that implements the Shape without modifying the existing code.*
|
||||
|
||||
```c#
|
||||
// Before
|
||||
public class Shape
|
||||
{
|
||||
public double CircleArea(double radius) { /*...*/ }
|
||||
|
||||
public double SquareArea(double sideLength) { /*...*/ }
|
||||
}
|
||||
|
||||
// After
|
||||
public abstract class Shape
|
||||
{
|
||||
public abstract double Area();
|
||||
}
|
||||
|
||||
public class Circle : Shape
|
||||
{
|
||||
public override double Area() { /*...*/ }
|
||||
}
|
||||
|
||||
public class Square : Shape
|
||||
{
|
||||
public override double Area() { /*...*/ }
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Liskov Substitution Principle (LSP):
|
||||
This Principle states that objects of a superclass should be substitutable with objects of its subclasses without affecting the correctness of the program. In simpler terms, **a subclass should behave in such a way that it does not break the functionality that the superclass expects**.
|
||||
|
||||
**Example**: *Consider a program that expects objects of type Bird. According to LSP, if you have a class Swan that inherits from Bird, you should be able to substitute an instance of Swan wherever you expect an Bird without breaking the program's functionality.*
|
||||
|
||||
```c#
|
||||
// Before
|
||||
public abstract class Bird
|
||||
{
|
||||
public abstract void Fly() { /* I can fly */}
|
||||
}
|
||||
|
||||
public abstract class Penguin : Bird
|
||||
{
|
||||
// Violating LSP principle (Penguin class breaks Fly functionality)
|
||||
public override void Fly()
|
||||
{
|
||||
throw new NotImplementedException("Penguins can't fly!");
|
||||
}
|
||||
}
|
||||
|
||||
// After
|
||||
public abstract class Bird
|
||||
{
|
||||
public abstract void Fly() { /* I can fly */}
|
||||
}
|
||||
|
||||
public abstract class Swan : Bird
|
||||
{
|
||||
public override void Fly() { /* I can fly */ }
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Interface Segregation Principle (ISP)
|
||||
This Principle suggests that clients should not be forced to depend on interfaces they do not use. In other words, **interfaces should be fine-grained and specific to the client's needs**. This involves breaking large interfaces into smaller, more focused interfaces.
|
||||
|
||||
**Example**: *Lets consider **IPerson** interface which has methods to work and eat. **Robot** class cannot implement IPerson interface as it cannot eat. IPerson should be splitted in to smaller interfaces like **IEater** and **IWorker** so Robot can implement IWorker.*
|
||||
```c#
|
||||
// Before
|
||||
public interface IPerson
|
||||
{
|
||||
void Work();
|
||||
void Eat();
|
||||
}
|
||||
|
||||
public class Robot : IPerson
|
||||
{
|
||||
public void Work() { /*...*/ }
|
||||
public void Eat() { /*...*/ } // Doesn't make sense for a robot
|
||||
}
|
||||
|
||||
// After
|
||||
public interface IWorker
|
||||
{
|
||||
void Work();
|
||||
}
|
||||
|
||||
public interface IEater
|
||||
{
|
||||
void Eat();
|
||||
}
|
||||
|
||||
public class Robot : IWorker
|
||||
{
|
||||
public void Work() { /*...*/ }
|
||||
}
|
||||
|
||||
public class Human : IEater, IWorker
|
||||
{
|
||||
public void Work() { /*...*/ }
|
||||
public void Eat() { /*...*/ }
|
||||
}
|
||||
```
|
||||
|
||||
## 5. Dependency Inversion Principle (DIP)
|
||||
This Principle states that high-level modules should not depend on low-level modules. Instead, **both should depend on abstractions**. This principle encourages the use of interfaces or abstract classes to decouple classes from their concrete implementations. Abstractions should not depend on details. Details should depend on abstractions.
|
||||
|
||||
**Example**: *If UserService directly depends on the concrete implementation of MySQLDatabase. This violates DIP since the high-level class UserService is directly dependent on a low-level class.
|
||||
**If we want to switch to a different database system (e.g., PostgreSQL), we need to modify the UserService class**.
|
||||
Instead of depending on concrete implementations, the high-level class UserService should depend on abstractions. Let's create a Database interface as an abstraction:*
|
||||
|
||||
```c#
|
||||
// Before
|
||||
|
||||
/* Low-level module */
|
||||
class MySQLDatabase {
|
||||
getUserData(id: number): string {
|
||||
// Logic to fetch user data from MySQL database
|
||||
}
|
||||
}
|
||||
|
||||
/* High-level module */
|
||||
class UserService {
|
||||
private database: MySQLDatabase;
|
||||
|
||||
constructor() {
|
||||
this.database = new MySQLDatabase();
|
||||
}
|
||||
|
||||
getUser(id: number): string {
|
||||
return this.database.getUserData(id);
|
||||
}
|
||||
}
|
||||
|
||||
// After
|
||||
|
||||
/* Abstract interface (abstraction) for the low-level module */
|
||||
interface Database {
|
||||
getUserData(id: number): string;
|
||||
}
|
||||
|
||||
/* low-level module implementing the Database interface */
|
||||
class MySQLDatabase implements Database {
|
||||
getUserData(id: number): string {}
|
||||
}
|
||||
|
||||
/* low-level module implementing the Database interface */
|
||||
class PostgreSQLDatabase implements Database {
|
||||
getUserData(id: number): string {}
|
||||
}
|
||||
|
||||
/* High-level module */
|
||||
class UserService {
|
||||
private database: Database;
|
||||
|
||||
constructor(database: Database) {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
getUser(id: number): string {
|
||||
return this.database.getUserData(id);
|
||||
}
|
||||
}
|
||||
```
|
||||
This way, the UserService class depends on the Database abstraction, not on concrete implementations, fulfilling the Dependency Inversion Principle.
|
||||
|
||||
**Note:** I'm excited to share this post that's like a treasure chest filled with nuggets of wisdom from different articles I've come across. Some of the examples taken from these articles [1](https://dev.to/galwaycoder/the-solid-principles-in-software-design-explained-53n) [2](https://dev.to/lukeskw/solid-principles-theyre-rock-solid-for-good-reason-31hn).
|
||||
143
_posts/2025-01-01-a-beginners-guide-to-ufw.md
Normal file
@ -0,0 +1,143 @@
|
||||
---
|
||||
title: "Keep Your Linux System Secure: A Beginner’s Guide to UFW"
|
||||
description: UFW is a tool that helps you decide which connections can or can't access your Linux system. It's simple enough for beginners but also has features for experts.
|
||||
date: 2025-01-01T17:48:18.319Z
|
||||
tags:
|
||||
- firewall
|
||||
- linux
|
||||
- security
|
||||
- ufw
|
||||
categories:
|
||||
- Linux
|
||||
- Firewall
|
||||
image:
|
||||
path: /assets/img/posts/ufw-logo.jpg
|
||||
alt: A Beginner's Guide to UFW (Uncomplicated Firewall)
|
||||
slug: ufw-guide
|
||||
---
|
||||
UFW (Uncomplicated Firewall) is a powerful yet user-friendly tool that allows you to control which connections can access your Linux system. Designed with simplicity in mind, it’s perfect for beginners while still offering advanced features for experienced users. In this post, we’ll explore how UFW works, how to use it effectively, and why it’s an essential tool for securing your system.
|
||||
|
||||
When it comes to securing your Linux server or desktop, managing network access is one of the most critical tasks. Firewalls serve as the first line of defense, and while tools like iptables offer granular control, they can be intimidating for beginners. That’s where UFW (Uncomplicated Firewall) steps in.
|
||||
|
||||
UFW is a user-friendly interface for managing iptables, designed to simplify the process of configuring a firewall. Whether you’re a seasoned Linux administrator or a newcomer, UFW makes securing your system straightforward and efficient.
|
||||
|
||||
## Why Use UFW?
|
||||
1. **Ease of Use:** UFW abstracts the complexities of iptables, offering simple commands to configure firewall rules.
|
||||
2. **Default Settings:** UFW ships with sensible defaults, such as denying all incoming connections while allowing outgoing ones.
|
||||
3. **Integration:** It integrates well with many linux distributions and is often installed by default.
|
||||
4. **Script-Friendly:** UFW is ideal for automation and scripting, making it perfect for managing servers at scale.
|
||||
|
||||
## Installing UFW
|
||||
Most modern Linux distributions come with UFW pre-installed. If it’s not already on your system, you can install it with the following commands:
|
||||
|
||||
For Ubuntu/Debian:
|
||||
```terminal
|
||||
sudo apt update
|
||||
sudo apt install ufw
|
||||
```
|
||||
For CentOS/RHEL:
|
||||
```terminal
|
||||
sudo yum install epel-release
|
||||
sudo yum install ufw
|
||||
```
|
||||
For Arch Linux:
|
||||
```terminal
|
||||
sudo pacman -S ufw
|
||||
```
|
||||
## Basic UFW Commands
|
||||
### Enable UFW
|
||||
Before configuring UFW, you need to enable it:
|
||||
```terminal
|
||||
sudo ufw enable
|
||||
```
|
||||
### Check UFW Status
|
||||
To see whether UFW is running and view current rules:
|
||||
```terminal
|
||||
sudo ufw status
|
||||
```
|
||||
### Allowing connections
|
||||
To allow traffic on a specific port, use the `allow` command. For example, to allow SSH connections:
|
||||
```terminal
|
||||
sudo ufw allow ssh
|
||||
```
|
||||
Or, specify the port number:
|
||||
```terminal
|
||||
sudo ufw allow 22
|
||||
```
|
||||
### Denying Connections
|
||||
To block traffic on a specific port:
|
||||
```terminal
|
||||
sudo ufw deny 80
|
||||
```
|
||||
### Removing Rules
|
||||
To delete a rule, prepend the rule with `delete`. For example:
|
||||
```terminal
|
||||
sudo ufw delete allow 22
|
||||
```
|
||||
Or Remove a rule by its number
|
||||
|
||||
List UFW Rules with Numbers
|
||||
```terminal
|
||||
sudo ufw status numbered
|
||||
```
|
||||
Example output
|
||||
```terminal
|
||||
Status: active
|
||||
|
||||
To Action From
|
||||
[ 1] 22/tcp ALLOW Anywhere
|
||||
[ 2] 80/tcp ALLOW Anywhere
|
||||
[ 3] 22/tcp (v6) ALLOW Anywhere (v6)
|
||||
[ 4] 80/tcp (v6) ALLOW Anywhere (v6)
|
||||
```
|
||||
Delete the Rule by Number
|
||||
```terminal
|
||||
sudo ufw delete 2
|
||||
```
|
||||
### Resetting UFW
|
||||
To reset UFW to its default state, removing all rules:
|
||||
```terminal
|
||||
sudo ufw reset
|
||||
```
|
||||
## Advanced Usage
|
||||
### Limiting Connections
|
||||
To protect against brute-force attacks, you can limit connections by using `limit` rule in UFW. This rule restricts the rate of new connections from the same IP address, allowing only a limited number of connections per minute (default: 6 attempts within 30 seconds). You can adjust these values by modifying the UFW configuration files, typically found in `/etc/ufw/` or `/etc/ufw/ufw.conf`, or by customizing rate limits using iptables rules directly. within a specified time frame. For instance, to limit SSH attempts, you can execute:
|
||||
```terminal
|
||||
sudo ufw limit ssh
|
||||
```
|
||||
This helps to deter malicious actors trying to gain unauthorized access to your system by repeatedly guessing passwords or exploiting vulnerabilities.
|
||||
### Allowing Specific IP Addresses
|
||||
To allow traffic from a specific IP address:
|
||||
```terminal
|
||||
sudo ufw allow from 192.168.0.100
|
||||
```
|
||||
Allowing Traffic to a Specific Port and IP
|
||||
For more granular control, you can specify both source IP and destination port:
|
||||
```terminal
|
||||
sudo ufw allow from 192.168.0.100 to any port 22
|
||||
sudo ufw allow from 192.168.0.0/24 to any port 22 proto tcp
|
||||
```
|
||||
### Using Application Profiles
|
||||
UFW supports application profiles to simplify rule management for common services. List available profiles with:
|
||||
```terminal
|
||||
sudo ufw app list
|
||||
```
|
||||
To allow a specific application, UFW provides predefined profiles for commonly used software and services. These profiles encapsulate the necessary port and protocol details, simplifying firewall configuration. For instance, to permit traffic for an application like Apache, you can execute:
|
||||
```terminal
|
||||
sudo ufw allow 'Apache Full'
|
||||
```
|
||||
This command enables both HTTP (port 80) and HTTPS (port 443) traffic, as defined in the application profile.
|
||||
|
||||
## Best Practices
|
||||
1. **Start with Defaults:** UFW's default policy denies incoming traffic and allows outgoing traffic, a good starting point for most setups.
|
||||
2. **Enable Logging:** Turn on logging to monitor blocked traffic:
|
||||
```terminal
|
||||
sudo ufw logging on
|
||||
```
|
||||
3. **Test Rules:** Before applying complex rules on a production system, test them in a safe environment.
|
||||
4. **Document Changes:** Keep a record of the rules you add or remove to make troubleshooting easier.
|
||||
|
||||
## Conclusion
|
||||
UFW makes it easy to manage your firewall, even if you’re new to Linux. By learning its simple and advanced features, you can protect your system without the hassle of complicated tools like iptables. Whether you’re using one computer or many servers, UFW is a great tool to keep things secure.
|
||||
|
||||
|
||||
@ -4,5 +4,23 @@ icon: fas fa-info-circle
|
||||
order: 4
|
||||
---
|
||||
|
||||
> Add Markdown syntax content to file `_tabs/about.md`{: .filepath } and it will show up on this page.
|
||||
{: .prompt-tip }
|
||||
## Meet Bangara Raju: Your Guide to Tech, DIY, and Beyond
|
||||
Hello there, curious minds and fellow explorers! I'm [**Bangara Raju**](https://bangararaju.kottedi.in/){:target="_blank"}, and I'm absolutely thrilled to welcome you to my corner of the internet.
|
||||
|
||||
### A Byte-Sized Introduction:
|
||||
By day, I'm immersed in the world of code and algorithms, working as a **Software Engineer** to shape the digital landscapes we navigate.
|
||||
|
||||
However, the realm of technology is just the beginning of my story. When the sun sets, you'll often find me elbow-deep in **DIY projects**, unraveling the mysteries of **automation**, and seeking out new skills to conquer.
|
||||
|
||||
### Why I'm Here:
|
||||
Why this blog, you might ask? The answer is simple: to share. **Sharing the knowledge** and insights I've gathered along my journey.
|
||||
|
||||
Whether it's demystifying complex tech concepts, guiding you through DIY **triumphs** (and the occasional **hiccups**), or embarking on new learning endeavors together, my goal is to foster a **community of learners and doers**.
|
||||
|
||||
### Exploring Tech, DIY, and Beyond:
|
||||
Through my **articles and stories**, I hope to guide you through the intricate world of technology, offer insights into automation's transformative power, unveil the secrets behind successful DIY projects, and encourage you to fearlessly **pursue new skills**.
|
||||
|
||||
Here's to a world of endless learning, unbounded creativity, and the **joy of sharing** it all with you.
|
||||
|
||||
Warm regards,
|
||||
**Bangara Raju**
|
||||
21
assets/css/heart.css
Normal file
@ -0,0 +1,21 @@
|
||||
.heart-icon {
|
||||
margin-bottom: -41px;
|
||||
margin-right: -40px;
|
||||
display: inline-flex;
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
transform: translate(-50%, -50%);
|
||||
background: url(../img/custom/heart.png) no-repeat;
|
||||
background-position: 0 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.heart-icon.liked {
|
||||
animation: like-anim 0.7s steps(28) forwards;
|
||||
}
|
||||
|
||||
@keyframes like-anim {
|
||||
to {
|
||||
background-position: right;
|
||||
}
|
||||
}
|
||||
BIN
assets/img/custom/heart.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 5.1 KiB |
13
assets/img/favicons/browserconfig.xml
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
layout: compress
|
||||
---
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="{{ '/assets/img/favicons/mstile-150x150.png' | relative_url }}" />
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
|
Before Width: | Height: | Size: 625 B After Width: | Height: | Size: 614 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1007 B |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 4.8 KiB |
122
assets/img/favicons/safari-pinned-tab.svg
Normal file
@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M970 4300 l0 -590 -215 0 -215 0 0 -130 0 -130 -159 0 -160 0 -20
|
||||
-26 c-19 -24 -21 -40 -21 -181 0 -150 1 -155 25 -178 24 -24 28 -25 180 -25
|
||||
l155 0 0 -1173 c0 -1292 -3 -1227 62 -1329 65 -103 178 -171 316 -189 50 -6
|
||||
642 -9 1702 -7 l1625 3 67 23 c81 27 157 82 209 152 73 97 69 19 69 1337 l0
|
||||
1183 155 0 c207 0 195 -12 195 204 0 218 12 205 -190 208 l-155 3 -3 132 -3
|
||||
133 -217 2 -217 3 -5 580 c-3 319 -6 581 -8 583 -1 1 -715 2 -1587 2 l-1585 0
|
||||
0 -590z m3110 -305 l0 -835 -121 0 c-112 0 -121 1 -126 20 -5 19 -14 20 -140
|
||||
20 -122 0 -134 -2 -131 -17 3 -17 -51 -18 -999 -21 -998 -2 -1003 -2 -1003 18
|
||||
0 19 -7 20 -135 20 -129 0 -135 -1 -135 -20 0 -20 -5 -20 -122 -18 l-123 3 -3
|
||||
820 c-1 451 0 826 3 833 3 9 317 12 1520 12 l1515 0 0 -835z m-3540 -655 l0
|
||||
-30 -150 0 -150 0 0 30 0 30 150 0 150 0 0 -30z m4330 0 l0 -30 -140 0 -140 0
|
||||
0 30 0 30 140 0 140 0 0 -30z m-3580 -255 c0 -13 22 -15 135 -15 114 0 135 2
|
||||
135 15 0 13 116 15 1003 13 795 -2 1001 -6 999 -15 -1 -10 30 -13 133 -13 113
|
||||
0 135 2 135 15 0 13 21 15 125 15 l125 0 0 -125 0 -125 -546 0 -547 0 6 -78
|
||||
c6 -93 28 -142 87 -196 65 -59 99 -69 252 -76 121 -6 142 -10 192 -34 133 -65
|
||||
161 -197 65 -301 -73 -80 -18 -76 -1045 -73 l-909 3 -48 28 c-150 88 -136 277
|
||||
25 347 44 19 78 24 193 29 130 6 144 9 193 36 95 52 142 135 142 252 l0 64
|
||||
-547 -1 -548 -2 -3 126 -3 126 126 0 c104 0 125 -2 125 -15z m-360 -1625 c16
|
||||
-16 20 -33 20 -87 0 -38 -4 -73 -8 -79 -15 -23 -53 -34 -115 -34 -50 0 -70 5
|
||||
-91 21 -24 19 -26 27 -26 89 0 96 16 110 122 110 65 0 81 -3 98 -20z m315 -5
|
||||
c21 -20 25 -34 25 -85 0 -51 -4 -65 -25 -85 -21 -22 -33 -25 -97 -25 -107 0
|
||||
-118 10 -118 110 0 100 11 110 118 110 64 0 76 -3 97 -25z m315 5 c16 -16 20
|
||||
-33 20 -89 0 -63 -2 -71 -26 -90 -38 -30 -150 -30 -188 0 -24 19 -26 27 -26
|
||||
89 0 96 16 110 122 110 65 0 81 -3 98 -20z m311 1 c21 -17 24 -28 24 -91 0
|
||||
-95 -16 -110 -120 -110 -104 0 -120 15 -120 110 0 63 3 74 24 91 33 27 159 27
|
||||
192 0z m319 -1 c16 -16 20 -33 20 -89 0 -63 -2 -71 -26 -90 -38 -30 -150 -30
|
||||
-188 0 -24 19 -26 27 -26 90 0 97 14 109 120 109 67 0 83 -3 100 -20z m311 1
|
||||
c21 -17 24 -28 24 -91 0 -95 -16 -110 -120 -110 -104 0 -120 15 -120 110 0 63
|
||||
3 74 24 91 33 27 159 27 192 0z m317 -1 c19 -18 22 -32 22 -90 0 -62 -2 -70
|
||||
-26 -89 -38 -30 -150 -30 -188 0 -24 19 -26 27 -26 90 0 96 14 109 118 109 65
|
||||
0 82 -3 100 -20z m310 4 c20 -13 22 -24 22 -94 0 -100 -11 -110 -116 -110 -99
|
||||
0 -116 15 -122 103 -4 60 -2 68 21 91 23 23 34 26 99 26 50 0 81 -5 96 -16z
|
||||
m320 -4 c19 -18 22 -32 22 -90 0 -62 -2 -70 -26 -89 -38 -30 -150 -30 -188 0
|
||||
-24 19 -26 27 -26 90 0 96 14 109 118 109 65 0 82 -3 100 -20z m310 4 c20 -13
|
||||
22 -24 22 -94 0 -100 -11 -110 -118 -110 -64 0 -76 3 -97 25 -34 33 -36 128
|
||||
-4 169 19 24 26 26 98 26 53 0 83 -5 99 -16z m319 -6 c19 -18 23 -32 23 -85 0
|
||||
-55 -4 -68 -25 -88 -21 -22 -33 -25 -90 -25 -64 0 -102 11 -117 34 -4 6 -8 41
|
||||
-8 79 0 94 14 107 117 107 65 0 80 -3 100 -22z m311 6 c19 -13 22 -24 22 -90
|
||||
0 -101 -13 -114 -115 -114 -67 0 -79 3 -100 25 -34 33 -36 128 -4 169 19 24
|
||||
26 26 98 26 53 0 83 -5 99 -16z m-3298 -274 c16 -16 20 -33 20 -89 0 -63 -2
|
||||
-71 -26 -90 -38 -30 -150 -30 -188 0 -24 19 -26 27 -26 90 0 97 14 109 120
|
||||
109 67 0 83 -3 100 -20z m314 1 c35 -39 33 -144 -5 -182 -15 -15 -33 -19 -94
|
||||
-19 -67 0 -79 3 -100 25 -34 33 -36 128 -4 169 19 24 26 26 103 26 68 0 86 -3
|
||||
100 -19z m316 -1 c16 -16 20 -33 20 -85 0 -57 -3 -69 -25 -90 -21 -22 -33 -25
|
||||
-93 -25 -55 0 -75 4 -96 21 -24 19 -26 26 -26 94 0 95 12 105 122 105 65 0 81
|
||||
-3 98 -20z m312 3 c14 -13 18 -31 18 -88 0 -100 -8 -109 -109 -113 -75 -4 -81
|
||||
-2 -105 22 -22 21 -26 34 -26 84 0 45 5 66 21 86 19 24 26 26 101 26 62 0 86
|
||||
-4 100 -17z m318 -3 c16 -16 20 -33 20 -85 0 -57 -3 -69 -25 -90 -21 -22 -33
|
||||
-25 -93 -25 -55 0 -75 4 -96 21 -24 19 -26 26 -26 94 0 95 12 105 122 105 65
|
||||
0 81 -3 98 -20z m312 3 c14 -13 18 -31 18 -87 0 -39 -4 -76 -8 -82 -15 -23
|
||||
-53 -34 -117 -34 -57 0 -69 3 -90 25 -21 20 -25 34 -25 86 0 51 4 67 22 86 19
|
||||
20 31 23 101 23 61 0 85 -4 99 -17z m318 -3 c16 -16 20 -33 20 -85 0 -57 -3
|
||||
-69 -25 -90 -21 -22 -33 -25 -90 -25 -64 0 -102 11 -117 34 -4 6 -8 43 -8 83
|
||||
0 93 12 103 122 103 65 0 81 -3 98 -20z m312 3 c14 -13 18 -31 18 -87 0 -39
|
||||
-4 -76 -8 -82 -15 -23 -53 -34 -117 -34 -57 0 -69 3 -90 25 -21 20 -25 34 -25
|
||||
80 0 55 12 93 34 107 6 4 47 8 90 8 60 0 84 -4 98 -17z m313 3 c15 -11 20 -30
|
||||
23 -84 4 -65 2 -73 -21 -96 -23 -23 -35 -26 -92 -26 -64 0 -102 11 -117 34 -4
|
||||
6 -8 43 -8 83 0 63 3 74 22 87 29 21 166 22 193 2z m313 -2 c19 -13 22 -24 22
|
||||
-87 0 -40 -4 -77 -8 -83 -15 -23 -53 -34 -117 -34 -57 0 -69 3 -90 25 -22 21
|
||||
-25 33 -25 90 0 92 15 105 118 105 54 0 84 -5 100 -16z m321 -10 c32 -41 30
|
||||
-136 -4 -169 -21 -22 -33 -25 -98 -25 -102 0 -117 14 -117 115 0 95 12 105
|
||||
121 105 72 0 79 -2 98 -26z m-3016 -245 c32 -14 47 -49 47 -104 0 -63 -14 -91
|
||||
-51 -104 -61 -21 -161 -9 -181 22 -4 7 -8 44 -8 83 0 72 9 92 50 107 27 10
|
||||
118 7 143 -4z m336 -13 c18 -16 21 -31 21 -91 0 -98 -13 -110 -122 -110 -101
|
||||
0 -118 15 -118 107 0 100 22 119 132 115 48 -2 72 -8 87 -21z m288 14 c36 -13
|
||||
53 -46 53 -105 0 -84 -33 -115 -125 -115 -65 0 -102 17 -114 52 -24 69 -7 142
|
||||
39 165 31 16 106 17 147 3z m344 -19 c27 -33 27 -139 0 -172 -17 -22 -27 -24
|
||||
-101 -24 -103 0 -120 15 -120 107 0 100 22 120 134 115 58 -2 71 -6 87 -26z
|
||||
m315 -4 c18 -21 24 -40 24 -77 0 -90 -32 -120 -125 -120 -92 0 -125 31 -125
|
||||
115 0 91 30 116 134 112 61 -2 71 -6 92 -30z m315 4 c27 -33 27 -139 0 -172
|
||||
-17 -22 -27 -24 -99 -24 -109 0 -122 12 -122 110 0 100 19 116 134 112 58 -2
|
||||
71 -6 87 -26z m308 5 c18 -16 21 -31 21 -90 0 -39 -4 -76 -8 -83 -19 -31 -120
|
||||
-43 -179 -23 -36 13 -53 46 -53 105 0 90 32 116 137 112 43 -2 68 -8 82 -21z
|
||||
m322 -5 c27 -33 27 -139 0 -172 -17 -22 -27 -24 -99 -24 -109 0 -122 12 -122
|
||||
110 0 100 19 116 134 112 58 -2 71 -6 87 -26z m308 5 c18 -16 21 -31 21 -90 0
|
||||
-39 -4 -76 -8 -83 -20 -31 -120 -43 -181 -22 -37 13 -51 41 -51 104 0 90 32
|
||||
116 137 112 43 -2 68 -8 82 -21z m322 -5 c27 -33 27 -139 0 -172 -17 -22 -27
|
||||
-24 -99 -24 -109 0 -122 12 -122 110 0 100 19 116 134 112 58 -2 71 -6 87 -26z
|
||||
m-303 -315 c2 -41 -1 -62 -10 -67 -7 -5 -551 -9 -1208 -9 -917 0 -1199 3
|
||||
-1208 12 -16 16 -16 115 1 121 6 3 554 5 1217 4 l1205 -2 3 -59z"/>
|
||||
<path d="M2613 4615 c-13 -9 -23 -23 -23 -32 0 -13 -9 -15 -62 -9 -117 14
|
||||
-111 15 -134 -34 -18 -40 -61 -247 -52 -255 3 -4 441 -93 476 -97 20 -3 26 7
|
||||
57 101 20 60 35 124 35 151 l0 47 -41 7 c-23 4 -51 13 -62 21 -12 8 -28 15
|
||||
-37 14 -10 0 -9 -3 5 -9 20 -8 20 -9 -1 -9 -29 -1 -34 7 -23 36 7 20 4 29 -16
|
||||
49 -13 13 -32 24 -42 24 -10 0 -27 3 -38 6 -11 2 -30 -2 -42 -11z m100 -37
|
||||
c15 -8 16 -12 6 -25 -10 -11 -23 -13 -53 -9 -51 9 -55 12 -49 37 5 18 11 20
|
||||
42 14 19 -4 44 -11 54 -17z"/>
|
||||
<path d="M1942 3923 c2 -11 13 -18 28 -19 53 -1 53 -2 48 -210 -4 -155 -2
|
||||
-196 8 -205 16 -13 48 6 38 22 -9 15 13 10 26 -6 20 -24 95 -30 146 -10 109
|
||||
41 140 218 53 301 -25 23 -40 29 -94 32 -48 3 -71 0 -90 -12 -13 -9 -27 -16
|
||||
-30 -16 -4 0 -3 32 1 70 l7 70 -72 0 c-64 0 -72 -2 -69 -17z m298 -161 c79
|
||||
-79 33 -232 -70 -232 -54 0 -86 31 -95 94 -15 101 23 166 97 166 30 0 46 -7
|
||||
68 -28z"/>
|
||||
<path d="M2342 3923 c2 -12 13 -18 33 -18 l30 0 3 -187 2 -188 -34 0 c-38 0
|
||||
-49 -9 -37 -28 5 -9 36 -12 107 -10 80 2 99 6 99 18 0 10 -13 17 -35 20 l-35
|
||||
5 0 203 0 202 -68 0 c-60 0 -68 -2 -65 -17z"/>
|
||||
<path d="M3145 3875 c-14 -13 -25 -29 -25 -35 0 -5 -20 -10 -45 -10 -26 0 -60
|
||||
-7 -76 -16 -37 -19 -62 -73 -50 -106 7 -18 3 -28 -20 -49 -36 -33 -36 -53 -4
|
||||
-85 l26 -26 -26 -20 c-35 -27 -35 -80 1 -113 24 -23 98 -45 149 -45 95 0 185
|
||||
50 185 103 0 66 -78 107 -203 107 -70 0 -104 16 -94 45 6 13 14 16 35 11 15
|
||||
-4 48 -9 73 -13 116 -14 190 90 120 169 -22 26 -27 38 -20 51 10 17 40 23 52
|
||||
10 4 -3 2 -12 -3 -18 -7 -9 -5 -16 6 -26 21 -17 48 0 52 34 7 55 -88 78 -133
|
||||
32z m-5 -105 c27 -27 26 -65 -2 -84 -32 -23 -94 -20 -118 4 -25 25 -25 41 1
|
||||
74 26 33 88 37 119 6z m38 -256 c22 -15 28 -39 14 -61 -20 -31 -128 -44 -191
|
||||
-22 -37 13 -52 52 -28 76 33 34 157 38 205 7z"/>
|
||||
<path d="M2625 3815 c-109 -57 -110 -245 -3 -310 48 -28 117 -30 167 -3 65 34
|
||||
85 66 89 147 4 75 -7 108 -53 151 -22 21 -38 25 -98 28 -51 2 -81 -2 -102 -13z
|
||||
m161 -48 c23 -20 28 -34 32 -86 7 -102 -29 -151 -113 -151 -99 0 -142 174 -58
|
||||
239 37 30 103 28 139 -2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.3 KiB |
26
assets/img/favicons/site.webmanifest
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
layout: compress
|
||||
---
|
||||
|
||||
{% assign favicon_path = "/assets/img/favicons" | relative_url %}
|
||||
|
||||
{
|
||||
"name": "{{ site.title }}",
|
||||
"short_name": "{{ site.title }}",
|
||||
"description": "{{ site.description }}",
|
||||
"icons": [
|
||||
{
|
||||
"src": "{{ favicon_path }}/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "{{ favicon_path }}/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}],
|
||||
"start_url": "{{ '/index.html' | relative_url }}",
|
||||
"theme_color": "#2a1e6b",
|
||||
"background_color": "#ffffff",
|
||||
"display": "fullscreen"
|
||||
}
|
||||
BIN
assets/img/posts/hello-world.jpg
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
assets/img/posts/python-getting-started.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
assets/img/posts/python-logo.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/img/posts/remark42.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
assets/img/posts/solid-principles.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
assets/img/posts/ufw-logo.jpg
Normal file
|
After Width: | Height: | Size: 38 KiB |
28
assets/js/comments/remark42.js
Normal file
@ -0,0 +1,28 @@
|
||||
var remark_config = {
|
||||
host: remark42Host,
|
||||
site_id: siteId,
|
||||
components: ['embed'],
|
||||
|
||||
max_shown_comments: 10,
|
||||
theme: Theme.visualState,
|
||||
|
||||
locale: 'en',
|
||||
show_email_subscription: false
|
||||
};
|
||||
|
||||
(function (c) {
|
||||
for (var i = 0; i < c.length; i++) {
|
||||
var d = document, s = d.createElement('script');
|
||||
s.src = remark_config.host + '/web/' + c[i] + '.js';
|
||||
s.defer = true;
|
||||
(d.head || d.body).appendChild(s);
|
||||
}
|
||||
})(remark_config.components || ['embed']);
|
||||
|
||||
var targetDiv = document.getElementById("sidebar").getElementsByClassName("mode-toggle")[0];
|
||||
|
||||
if (targetDiv != null) {
|
||||
targetDiv.addEventListener("click", () => {
|
||||
window.REMARK42.changeTheme(Theme.visualState === 'light' ? 'dark' : 'light');
|
||||
})
|
||||
}
|
||||
19
assets/js/common.js
Normal file
@ -0,0 +1,19 @@
|
||||
var serviceUrl = document.currentScript.getAttribute('serviceurl');
|
||||
var siteId = document.currentScript.getAttribute('siteid');
|
||||
var postId = document.currentScript.getAttribute('postid');
|
||||
var remark42Host = document.currentScript.getAttribute('remark42host');
|
||||
|
||||
function ajax(url, type, payload) {
|
||||
if (payload === undefined) {
|
||||
payload = null;
|
||||
}
|
||||
return new Promise(function (resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onload = function () {
|
||||
resolve(this.responseText);
|
||||
};
|
||||
xhr.onerror = reject;
|
||||
xhr.open(type, url);
|
||||
xhr.send(payload);
|
||||
});
|
||||
}
|
||||
45
assets/js/likes.js
Normal file
@ -0,0 +1,45 @@
|
||||
const heartIcon = document.querySelector(".heart-icon");
|
||||
const likesCount = document.getElementById("likes-count");
|
||||
const likesLoader = document.getElementById("likes-loader");
|
||||
|
||||
likesLoader.style.display = "none"
|
||||
|
||||
let likesAmount = likesCount.innerHTML;
|
||||
|
||||
function onLoadAndClick(){
|
||||
likesLoader.style.display = "inline-flex"
|
||||
likesCount.style.display = "none";
|
||||
heartIcon.style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
function apiCall(url, event) {
|
||||
onLoadAndClick();
|
||||
ajax(url)
|
||||
.then(function (result) {
|
||||
if(event === "click"){
|
||||
heartIcon.classList.toggle("liked");
|
||||
}
|
||||
likesCount.innerHTML = result;
|
||||
likesLoader.style.display = "none"
|
||||
likesCount.style.display = "inline-flex";
|
||||
heartIcon.style.pointerEvents = 'auto';
|
||||
})
|
||||
.catch(function () {
|
||||
likesLoader.style.display = "none";
|
||||
likesCount.style.display = "inline-flex";
|
||||
});
|
||||
}
|
||||
|
||||
var apiUrl = serviceUrl + "/GetPostLikes?siteId=" + siteId + "&postId=" + postId;
|
||||
|
||||
apiCall(apiUrl);
|
||||
|
||||
heartIcon.addEventListener("click", () => {
|
||||
if (!heartIcon.classList.contains("liked")) {
|
||||
apiUrl = serviceUrl + "/LikePost?siteId=" + siteId + "&postId=" + postId;
|
||||
} else {
|
||||
apiUrl = serviceUrl + "/DislikePost?siteId=" + siteId + "&postId=" + postId;
|
||||
}
|
||||
|
||||
apiCall(apiUrl, "click");
|
||||
});
|
||||
16
assets/js/views.js
Normal file
@ -0,0 +1,16 @@
|
||||
const viewsCount = document.getElementById("views-count");
|
||||
const viewsLoader = document.getElementById("views-loader");
|
||||
|
||||
viewsCount.style.display = "none";
|
||||
viewsLoader.style.display = "inline-block"
|
||||
|
||||
ajax(serviceUrl + "/GetPostViews?siteId=" + siteId + "&postId=" + postId)
|
||||
.then(function (result) {
|
||||
viewsCount.innerText = result;
|
||||
viewsLoader.style.display = "none";
|
||||
viewsCount.style.display = "inline-block";
|
||||
})
|
||||
.catch(function () {
|
||||
viewsLoader.style.display = "none";
|
||||
viewsCount.style.display = "inline-block";
|
||||
});
|
||||
9
db_scripts/PostLikeCounter.sql
Normal file
@ -0,0 +1,9 @@
|
||||
CREATE TABLE `PostLikeCounter` (
|
||||
`id` int(20) NOT NULL AUTO_INCREMENT,
|
||||
`siteid` varchar(500),
|
||||
`postid` varchar(500),
|
||||
`count` int(20),
|
||||
`datecreated` timestamp DEFAULT current_timestamp() ,
|
||||
`datemodified` timestamp DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||
PRIMARY KEY (`id`)
|
||||
)
|
||||
9
db_scripts/PostVisitCounter.sql
Normal file
@ -0,0 +1,9 @@
|
||||
CREATE TABLE `PostVisitCounter` (
|
||||
`id` int(20) NOT NULL AUTO_INCREMENT,
|
||||
`siteid` varchar(500),
|
||||
`postid` varchar(500),
|
||||
`count` int(20),
|
||||
`datecreated` timestamp DEFAULT current_timestamp() ,
|
||||
`datemodified` timestamp DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||
PRIMARY KEY (`id`)
|
||||
)
|
||||
22
db_scripts/mysql.sql
Normal file
@ -0,0 +1,22 @@
|
||||
-- Create USER
|
||||
|
||||
-- CREATE USER 'user1'@localhost IDENTIFIED BY 'password1';
|
||||
|
||||
CREATE USER blog_admin@'%' IDENTIFIED BY 'pass@1234';
|
||||
|
||||
GRANT ALL PRIVILEGES ON blogDB.* TO blog_admin@'192.168.0.%';
|
||||
GRANT ALL PRIVILEGES ON blogDB.* TO blog_admin@'10.1.0.%';
|
||||
|
||||
SHOW GRANTS FOR blog_admin@'%';
|
||||
|
||||
FLUSH PRIVILEGES;
|
||||
|
||||
-- Create Counter Table
|
||||
|
||||
-- DROP TABLE PostVisitCounter
|
||||
|
||||
-- DROP TABLE PostLikeCounter
|
||||
|
||||
-- create table PostVisitCounter (Id INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, SiteId nvarchar(500), PostId nvarchar(500), Count INT(20), DateCreated TIMESTAMP, DateModified TIMESTAMP);
|
||||
|
||||
-- create table PostLikeCounter (Id INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, SiteId nvarchar(500), PostId nvarchar(500), Count INT(20), DateCreated TIMESTAMP, DateModified TIMESTAMP);
|
||||
75
frontmatter.json
Normal file
@ -0,0 +1,75 @@
|
||||
{
|
||||
"$schema": "https://frontmatter.codes/frontmatter.schema.json",
|
||||
"frontMatter.taxonomy.contentTypes": [
|
||||
{
|
||||
"name": "default",
|
||||
"pageBundle": false,
|
||||
"previewPath": null,
|
||||
"fields": [
|
||||
{
|
||||
"title": "Title",
|
||||
"name": "title",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Description",
|
||||
"name": "description",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "Publishing date",
|
||||
"name": "date",
|
||||
"type": "datetime",
|
||||
"default": "{{now}}",
|
||||
"isPublishDate": true
|
||||
},
|
||||
{
|
||||
"title": "Tags",
|
||||
"name": "tags",
|
||||
"type": "tags"
|
||||
},
|
||||
{
|
||||
"title": "Categories",
|
||||
"name": "categories",
|
||||
"type": "categories"
|
||||
},
|
||||
{
|
||||
"title": "image",
|
||||
"name": "image",
|
||||
"type": "fields",
|
||||
"fields": [
|
||||
{
|
||||
"title": "path",
|
||||
"name": "path",
|
||||
"type": "image"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "disable-ty",
|
||||
"name": "disable-ty",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"title": "series",
|
||||
"name": "series",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"frontMatter.framework.id": "jekyll",
|
||||
"frontMatter.content.publicFolder": "",
|
||||
"frontMatter.preview.host": "http://localhost:4000",
|
||||
"frontMatter.content.pageFolders": [
|
||||
{
|
||||
"title": "drafts",
|
||||
"path": "[[workspace]]/_drafts"
|
||||
},
|
||||
{
|
||||
"title": "posts",
|
||||
"path": "[[workspace]]/_posts"
|
||||
}
|
||||
],
|
||||
"frontMatter.git.enabled": true
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
---
|
||||
layout: home
|
||||
# Index page
|
||||
---
|
||||
---
|
||||
54
tools/run.sh
Normal file
@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Run jekyll serve and then launch the site
|
||||
|
||||
prod=false
|
||||
command="bundle exec jekyll s -l"
|
||||
host="127.0.0.1"
|
||||
|
||||
help() {
|
||||
echo "Usage:"
|
||||
echo
|
||||
echo " bash /path/to/run [options]"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -H, --host [HOST] Host to bind to."
|
||||
echo " -p, --production Run Jekyll in 'production' mode."
|
||||
echo " -h, --help Print this help information."
|
||||
}
|
||||
|
||||
while (($#)); do
|
||||
opt="$1"
|
||||
case $opt in
|
||||
-H | --host)
|
||||
host="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p | --production)
|
||||
prod=true
|
||||
shift
|
||||
;;
|
||||
-h | --help)
|
||||
help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo -e "> Unknown option: '$opt'\n"
|
||||
help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
command="$command -H $host"
|
||||
|
||||
if $prod; then
|
||||
command="JEKYLL_ENV=production $command"
|
||||
fi
|
||||
|
||||
if [ -e /proc/1/cgroup ] && grep -q docker /proc/1/cgroup; then
|
||||
command="$command --force_polling"
|
||||
fi
|
||||
|
||||
echo -e "\n> $command\n"
|
||||
eval "$command"
|
||||
89
tools/test.sh
Normal file
@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Build and test the site content
|
||||
#
|
||||
# Requirement: html-proofer, jekyll
|
||||
#
|
||||
# Usage: See help information
|
||||
|
||||
set -eu
|
||||
|
||||
SITE_DIR="_site"
|
||||
|
||||
_config="_config.yml"
|
||||
|
||||
_baseurl=""
|
||||
|
||||
help() {
|
||||
echo "Build and test the site content"
|
||||
echo
|
||||
echo "Usage:"
|
||||
echo
|
||||
echo " bash $0 [options]"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo ' -c, --config "<config_a[,config_b[...]]>" Specify config file(s)'
|
||||
echo " -h, --help Print this information."
|
||||
}
|
||||
|
||||
read_baseurl() {
|
||||
if [[ $_config == *","* ]]; then
|
||||
# multiple config
|
||||
IFS=","
|
||||
read -ra config_array <<<"$_config"
|
||||
|
||||
# reverse loop the config files
|
||||
for ((i = ${#config_array[@]} - 1; i >= 0; i--)); do
|
||||
_tmp_baseurl="$(grep '^baseurl:' "${config_array[i]}" | sed "s/.*: *//;s/['\"]//g;s/#.*//")"
|
||||
|
||||
if [[ -n $_tmp_baseurl ]]; then
|
||||
_baseurl="$_tmp_baseurl"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
else
|
||||
# single config
|
||||
_baseurl="$(grep '^baseurl:' "$_config" | sed "s/.*: *//;s/['\"]//g;s/#.*//")"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
# clean up
|
||||
if [[ -d $SITE_DIR ]]; then
|
||||
rm -rf "$SITE_DIR"
|
||||
fi
|
||||
|
||||
read_baseurl
|
||||
|
||||
# build
|
||||
JEKYLL_ENV=production bundle exec jekyll b \
|
||||
-d "$SITE_DIR$_baseurl" -c "$_config"
|
||||
|
||||
# test
|
||||
bundle exec htmlproofer "$SITE_DIR" \
|
||||
--disable-external \
|
||||
--ignore-urls "/^http:\/\/127.0.0.1/,/^http:\/\/0.0.0.0/,/^http:\/\/localhost/"
|
||||
}
|
||||
|
||||
while (($#)); do
|
||||
opt="$1"
|
||||
case $opt in
|
||||
-c | --config)
|
||||
_config="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-h | --help)
|
||||
help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
# unknown option
|
||||
help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
main
|
||||