feat: use hexo
@ -1,2 +0,0 @@
|
||||
node_modules
|
||||
vendor
|
@ -1,31 +1,28 @@
|
||||
---
|
||||
new_page_extension: md
|
||||
auto_deploy: false
|
||||
admin_path: ''
|
||||
admin_path: ""
|
||||
webhook_url:
|
||||
sections:
|
||||
- type: jekyll-posts
|
||||
- type: directory
|
||||
path: source/_posts
|
||||
label: Posts
|
||||
create: documents
|
||||
templates:
|
||||
- blog-post
|
||||
match: "**/*"
|
||||
- type: directory
|
||||
path: _pages
|
||||
path: source/_pages
|
||||
label: Pages
|
||||
create: documents
|
||||
match: "**/*"
|
||||
upload_dir: _uploads
|
||||
upload_dir: source/uploads
|
||||
public_path: "/uploads"
|
||||
front_matter_path: ''
|
||||
front_matter_path: ""
|
||||
use_front_matter_path: false
|
||||
file_template: ":filename:"
|
||||
build:
|
||||
preview_env:
|
||||
- JEKYLL_ENV=staging
|
||||
preview_output_directory: _site
|
||||
install_dependencies_command: bundle install --path vendor/bundle
|
||||
preview_output_directory: public
|
||||
install_dependencies_command: npm i
|
||||
preview_docker_image: forestryio/ruby:2.6
|
||||
mount_path: "/srv"
|
||||
working_dir: "/srv"
|
||||
instant_preview_command: bundle exec jekyll serve --drafts --unpublished --future
|
||||
--port 8080 --host 0.0.0.0 -d _site
|
||||
instant_preview_command: yarn server
|
||||
|
34
.github/workflows/pages.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
# https://hexo.io/docs/github-pages.html
|
||||
name: Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- source # default branch
|
||||
|
||||
jobs:
|
||||
pages:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 12.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: "12.x"
|
||||
- name: Cache NPM dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.OS }}-npm-cache
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-npm-cache
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
- name: Build
|
||||
run: npm run build
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./public
|
||||
publish_branch: master # deploying branch
|
8
.gitignore
vendored
@ -7,3 +7,11 @@
|
||||
/images/crushed
|
||||
/.bundle
|
||||
/vendor/bundle/
|
||||
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
db.json
|
||||
*.log
|
||||
public/
|
||||
.deploy*/
|
||||
yarn.lock
|
2
.prettierignore
Normal file
@ -0,0 +1,2 @@
|
||||
scaffolds/*
|
||||
*.ejs
|
@ -12,9 +12,7 @@
|
||||
},
|
||||
"filters": {
|
||||
"whitelist": {
|
||||
"allow": [
|
||||
"/{%.+?%}/"
|
||||
]
|
||||
"allow": ["/{%.+?%}/"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3
Gemfile
@ -1,3 +0,0 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'github-pages', group: :jekyll_plugins # workaround https://github.com/github/pages-gem#usage
|
258
Gemfile.lock
@ -1,258 +0,0 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (6.0.3.3)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
zeitwerk (~> 2.2, >= 2.2.2)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.11.1)
|
||||
colorator (1.1.0)
|
||||
commonmarker (0.17.13)
|
||||
ruby-enum (~> 0.5)
|
||||
concurrent-ruby (1.1.7)
|
||||
dnsruby (1.61.4)
|
||||
simpleidn (~> 0.1)
|
||||
em-websocket (0.5.2)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
ethon (0.12.0)
|
||||
ffi (>= 1.3.0)
|
||||
eventmachine (1.2.7)
|
||||
execjs (2.7.0)
|
||||
faraday (1.0.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffi (1.13.1)
|
||||
forwardable-extended (2.6.0)
|
||||
gemoji (3.0.1)
|
||||
github-pages (207)
|
||||
github-pages-health-check (= 1.16.1)
|
||||
jekyll (= 3.9.0)
|
||||
jekyll-avatar (= 0.7.0)
|
||||
jekyll-coffeescript (= 1.1.1)
|
||||
jekyll-commonmark-ghpages (= 0.1.6)
|
||||
jekyll-default-layout (= 0.1.4)
|
||||
jekyll-feed (= 0.13.0)
|
||||
jekyll-gist (= 1.5.0)
|
||||
jekyll-github-metadata (= 2.13.0)
|
||||
jekyll-mentions (= 1.5.1)
|
||||
jekyll-optional-front-matter (= 0.3.2)
|
||||
jekyll-paginate (= 1.1.0)
|
||||
jekyll-readme-index (= 0.3.0)
|
||||
jekyll-redirect-from (= 0.15.0)
|
||||
jekyll-relative-links (= 0.6.1)
|
||||
jekyll-remote-theme (= 0.4.1)
|
||||
jekyll-sass-converter (= 1.5.2)
|
||||
jekyll-seo-tag (= 2.6.1)
|
||||
jekyll-sitemap (= 1.4.0)
|
||||
jekyll-swiss (= 1.0.0)
|
||||
jekyll-theme-architect (= 0.1.1)
|
||||
jekyll-theme-cayman (= 0.1.1)
|
||||
jekyll-theme-dinky (= 0.1.1)
|
||||
jekyll-theme-hacker (= 0.1.1)
|
||||
jekyll-theme-leap-day (= 0.1.1)
|
||||
jekyll-theme-merlot (= 0.1.1)
|
||||
jekyll-theme-midnight (= 0.1.1)
|
||||
jekyll-theme-minimal (= 0.1.1)
|
||||
jekyll-theme-modernist (= 0.1.1)
|
||||
jekyll-theme-primer (= 0.5.4)
|
||||
jekyll-theme-slate (= 0.1.1)
|
||||
jekyll-theme-tactile (= 0.1.1)
|
||||
jekyll-theme-time-machine (= 0.1.1)
|
||||
jekyll-titles-from-headings (= 0.5.3)
|
||||
jemoji (= 0.11.1)
|
||||
kramdown (= 2.3.0)
|
||||
kramdown-parser-gfm (= 1.1.0)
|
||||
liquid (= 4.0.3)
|
||||
mercenary (~> 0.3)
|
||||
minima (= 2.5.1)
|
||||
nokogiri (>= 1.10.4, < 2.0)
|
||||
rouge (= 3.19.0)
|
||||
terminal-table (~> 1.4)
|
||||
github-pages-health-check (1.16.1)
|
||||
addressable (~> 2.3)
|
||||
dnsruby (~> 1.60)
|
||||
octokit (~> 4.0)
|
||||
public_suffix (~> 3.0)
|
||||
typhoeus (~> 1.3)
|
||||
html-pipeline (2.14.0)
|
||||
activesupport (>= 2)
|
||||
nokogiri (>= 1.4)
|
||||
http_parser.rb (0.6.0)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jekyll (3.9.0)
|
||||
addressable (~> 2.4)
|
||||
colorator (~> 1.0)
|
||||
em-websocket (~> 0.5)
|
||||
i18n (~> 0.7)
|
||||
jekyll-sass-converter (~> 1.0)
|
||||
jekyll-watch (~> 2.0)
|
||||
kramdown (>= 1.17, < 3)
|
||||
liquid (~> 4.0)
|
||||
mercenary (~> 0.3.3)
|
||||
pathutil (~> 0.9)
|
||||
rouge (>= 1.7, < 4)
|
||||
safe_yaml (~> 1.0)
|
||||
jekyll-avatar (0.7.0)
|
||||
jekyll (>= 3.0, < 5.0)
|
||||
jekyll-coffeescript (1.1.1)
|
||||
coffee-script (~> 2.2)
|
||||
coffee-script-source (~> 1.11.1)
|
||||
jekyll-commonmark (1.3.1)
|
||||
commonmarker (~> 0.14)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-commonmark-ghpages (0.1.6)
|
||||
commonmarker (~> 0.17.6)
|
||||
jekyll-commonmark (~> 1.2)
|
||||
rouge (>= 2.0, < 4.0)
|
||||
jekyll-default-layout (0.1.4)
|
||||
jekyll (~> 3.0)
|
||||
jekyll-feed (0.13.0)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-gist (1.5.0)
|
||||
octokit (~> 4.2)
|
||||
jekyll-github-metadata (2.13.0)
|
||||
jekyll (>= 3.4, < 5.0)
|
||||
octokit (~> 4.0, != 4.4.0)
|
||||
jekyll-mentions (1.5.1)
|
||||
html-pipeline (~> 2.3)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-optional-front-matter (0.3.2)
|
||||
jekyll (>= 3.0, < 5.0)
|
||||
jekyll-paginate (1.1.0)
|
||||
jekyll-readme-index (0.3.0)
|
||||
jekyll (>= 3.0, < 5.0)
|
||||
jekyll-redirect-from (0.15.0)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-relative-links (0.6.1)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-remote-theme (0.4.1)
|
||||
addressable (~> 2.0)
|
||||
jekyll (>= 3.5, < 5.0)
|
||||
rubyzip (>= 1.3.0)
|
||||
jekyll-sass-converter (1.5.2)
|
||||
sass (~> 3.4)
|
||||
jekyll-seo-tag (2.6.1)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-sitemap (1.4.0)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-swiss (1.0.0)
|
||||
jekyll-theme-architect (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-cayman (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-dinky (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-hacker (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-leap-day (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-merlot (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-midnight (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-minimal (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-modernist (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-primer (0.5.4)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-github-metadata (~> 2.9)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-slate (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-tactile (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-time-machine (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-titles-from-headings (0.5.3)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-watch (2.2.1)
|
||||
listen (~> 3.0)
|
||||
jemoji (0.11.1)
|
||||
gemoji (~> 3.0)
|
||||
html-pipeline (~> 2.2)
|
||||
jekyll (>= 3.0, < 5.0)
|
||||
kramdown (2.3.0)
|
||||
rexml
|
||||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
liquid (4.0.3)
|
||||
listen (3.2.1)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
mercenary (0.3.6)
|
||||
mini_portile2 (2.4.0)
|
||||
minima (2.5.1)
|
||||
jekyll (>= 3.5, < 5.0)
|
||||
jekyll-feed (~> 0.9)
|
||||
jekyll-seo-tag (~> 2.1)
|
||||
minitest (5.14.2)
|
||||
multipart-post (2.1.1)
|
||||
nokogiri (1.10.10)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
octokit (4.18.0)
|
||||
faraday (>= 0.9)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (3.1.1)
|
||||
rb-fsevent (0.10.4)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rexml (3.2.4)
|
||||
rouge (3.19.0)
|
||||
ruby-enum (0.8.0)
|
||||
i18n
|
||||
rubyzip (2.3.0)
|
||||
safe_yaml (1.0.5)
|
||||
sass (3.7.4)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
sawyer (0.8.2)
|
||||
addressable (>= 2.3.5)
|
||||
faraday (> 0.8, < 2.0)
|
||||
simpleidn (0.1.1)
|
||||
unf (~> 0.1.4)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thread_safe (0.3.6)
|
||||
typhoeus (1.4.0)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (1.2.7)
|
||||
thread_safe (~> 0.1)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (1.7.0)
|
||||
zeitwerk (2.4.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
github-pages
|
||||
|
||||
BUNDLED WITH
|
||||
2.1.4
|
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Yasuaki Uechi (https://uechi.io)
|
||||
Copyright (c) 2020 Yasuaki Uechi (https://uechi.io)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -5,15 +5,13 @@
|
||||
## Build
|
||||
|
||||
```
|
||||
docker-compose run web bundle install
|
||||
docker-compose up --build
|
||||
yarn build
|
||||
```
|
||||
|
||||
## Test
|
||||
## Proofread
|
||||
|
||||
```
|
||||
npm install
|
||||
npm test
|
||||
yarn test
|
||||
```
|
||||
|
||||
## Publish
|
||||
|
179
_config.yml
@ -1,47 +1,138 @@
|
||||
---
|
||||
# Hexo Configuration
|
||||
## Docs: https://hexo.io/docs/configuration.html
|
||||
## Source: https://github.com/hexojs/hexo/
|
||||
|
||||
# Site
|
||||
title: uechi.io
|
||||
timezone: Asia/Tokyo
|
||||
collections:
|
||||
posts:
|
||||
title: Posts
|
||||
output: true
|
||||
uploads:
|
||||
title: Uploads
|
||||
output: true
|
||||
defaults:
|
||||
- scope:
|
||||
path: ''
|
||||
type: posts
|
||||
values:
|
||||
layout: post
|
||||
permalink: "/blog/:title"
|
||||
description: Random posts from @uetchy.
|
||||
url: https://uechi.io
|
||||
email: y@uechi.io
|
||||
twitter:
|
||||
username: uechz
|
||||
subtitle: ""
|
||||
description: "Random posts from U"
|
||||
keywords:
|
||||
author: Yasuaki Uechi
|
||||
language: en
|
||||
timezone: "Asia/Tokyo"
|
||||
|
||||
excerpt:
|
||||
depth: 1
|
||||
|
||||
github:
|
||||
username: uetchy
|
||||
logo: "/images/icon.png"
|
||||
excerpt_separator: "#"
|
||||
google_analytics: UA-28919359-12
|
||||
plugins:
|
||||
- jekyll-redirect-from
|
||||
- jekyll-sitemap
|
||||
- jekyll-seo-tag
|
||||
- jekyll-feed
|
||||
- jemoji
|
||||
include:
|
||||
- _pages
|
||||
exclude:
|
||||
- README.md
|
||||
- LICENSE
|
||||
- CNAME
|
||||
- Gemfile
|
||||
- Gemfile.lock
|
||||
- package.json
|
||||
- node_modules
|
||||
- script
|
||||
- vendor
|
||||
- docker-compose.yml
|
||||
- Dockerfile
|
||||
twitter:
|
||||
username: uechz
|
||||
|
||||
umami:
|
||||
host: analytics.uechi.io
|
||||
id: 2739f9aa-b8d5-45fa-8972-07a5bbb87e8a
|
||||
|
||||
node_sass:
|
||||
outputStyle: nested
|
||||
precision: 5
|
||||
sourceComments: false
|
||||
|
||||
# URL
|
||||
## If your site is put in a subdirectory, set url as 'http://example.com/child' and root as '/child/'
|
||||
url: https://uechi.io
|
||||
root: /
|
||||
permalink: /blog/:name/
|
||||
permalink_defaults:
|
||||
pretty_urls:
|
||||
trailing_index: true # Set to false to remove trailing 'index.html' from permalinks
|
||||
trailing_html: true # Set to false to remove trailing '.html' from permalinks
|
||||
|
||||
# Directory
|
||||
source_dir: source
|
||||
public_dir: public
|
||||
tag_dir: tags
|
||||
archive_dir: archives
|
||||
category_dir: categories
|
||||
code_dir: downloads/code
|
||||
i18n_dir: :lang
|
||||
skip_render:
|
||||
|
||||
# Writing
|
||||
new_post_name: :title.md # File name of new posts
|
||||
default_layout: post
|
||||
titlecase: false # Transform title into titlecase
|
||||
external_link:
|
||||
enable: true # Open external links in new tab
|
||||
field: site # Apply to the whole site
|
||||
exclude: ""
|
||||
filename_case: 0
|
||||
render_drafts: false
|
||||
relative_link: false
|
||||
future: true
|
||||
post_asset_folder: true
|
||||
marked:
|
||||
prependRoot: true
|
||||
postAsset: true
|
||||
|
||||
# Highlight
|
||||
highlight:
|
||||
enable: true
|
||||
line_number: false
|
||||
auto_detect: false
|
||||
tab_replace: ""
|
||||
wrap: false
|
||||
hljs: true
|
||||
prismjs:
|
||||
enable: false
|
||||
|
||||
# Home page setting
|
||||
# path: Root path for your blogs index page. (default = '')
|
||||
# per_page: Posts displayed per page. (0 = disable pagination)
|
||||
# order_by: Posts order. (Order by date descending by default)
|
||||
index_generator:
|
||||
path: ""
|
||||
per_page: 20
|
||||
order_by: -date
|
||||
|
||||
# Category & Tag
|
||||
default_category: uncategorized
|
||||
category_map:
|
||||
tag_map:
|
||||
|
||||
# Metadata elements
|
||||
## https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
|
||||
meta_generator: true
|
||||
|
||||
# Date / Time format
|
||||
## Hexo uses Moment.js to parse and display date
|
||||
## You can customize the date format as defined in
|
||||
## http://momentjs.com/docs/#/displaying/format/
|
||||
date_format: YYYY-MM-DD
|
||||
time_format: HH:mm:ss
|
||||
## updated_option supports 'mtime', 'date', 'empty'
|
||||
updated_option: "mtime"
|
||||
|
||||
# Pagination
|
||||
## Set per_page to 0 to disable pagination
|
||||
per_page: 20
|
||||
pagination_dir: page
|
||||
|
||||
# Include / Exclude file(s)
|
||||
## include:/exclude: options only apply to the 'source/' folder
|
||||
# include:
|
||||
# exclude:
|
||||
ignore:
|
||||
|
||||
# Extensions
|
||||
## Plugins: https://hexo.io/plugins/
|
||||
## Themes: https://hexo.io/themes/
|
||||
theme: vanilla
|
||||
|
||||
# Deployment
|
||||
## Docs: https://hexo.io/docs/one-command-deployment
|
||||
deploy:
|
||||
type: "github_pages"
|
||||
|
||||
mathjax:
|
||||
tags: none # or 'ams' or 'all'
|
||||
single_dollars: true # enable single dollar signs as in-line math delimiters
|
||||
cjk_width: 0.9 # relative CJK char width
|
||||
normal_width: 0.6 # relative normal (monospace) width
|
||||
append_css: true # add CSS to pages rendered by MathJax
|
||||
every_page: true # if true, every page will be rendered by MathJax regardless the `mathjax` setting in Front-matter
|
||||
|
||||
feed:
|
||||
type: atom
|
||||
path: feed.xml
|
||||
autodiscovery: true
|
||||
|
@ -1,14 +0,0 @@
|
||||
---
|
||||
date: 2020-02-13 16:22:05 +0900
|
||||
title: 静寂を得る方法
|
||||
|
||||
---
|
||||
聴覚過敏であったり、そうでなくとも周りの音がパフォーマンスに悪影響となる人のために、静寂を得る方法を紹介します。
|
||||
|
||||
## EARIN M-2
|
||||
|
||||
[EARIN](https://earin.com/) は左右分離型Bluetoothイヤホンです。付属のイヤホンの代わりに自分の耳にフィットするComplyのイヤーチップと付け替えます。
|
||||
|
||||
## Moldex
|
||||
|
||||
Moldex は使い捨て耳栓のメーカーであり、各種遮音レベルに分かれた多様なラインナップを提供しています。
|
@ -1,25 +0,0 @@
|
||||
<!-- Google Analytics -->
|
||||
<script>
|
||||
;(function(i, s, o, g, r, a, m) {
|
||||
i['GoogleAnalyticsObject'] = r
|
||||
;(i[r] =
|
||||
i[r] ||
|
||||
function() {
|
||||
;(i[r].q = i[r].q || []).push(arguments)
|
||||
}),
|
||||
(i[r].l = 1 * new Date())
|
||||
;(a = s.createElement(o)), (m = s.getElementsByTagName(o)[0])
|
||||
a.async = 1
|
||||
a.src = g
|
||||
m.parentNode.insertBefore(a, m)
|
||||
})(
|
||||
window,
|
||||
document,
|
||||
'script',
|
||||
'https://www.google-analytics.com/analytics.js',
|
||||
'ga'
|
||||
)
|
||||
|
||||
ga('create', '{{ site.google_analytics }}', 'auto')
|
||||
ga('send', 'pageview')
|
||||
</script>
|
@ -1,53 +0,0 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<link rel="shortcut icon" href="{{ site.baseurl }}/images/favicon.ico" />
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="{{ site.baseurl }}/images/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="{{ site.baseurl }}/images/favicon-32x32.png"
|
||||
sizes="32x32"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="{{ site.baseurl }}/images/favicon-16x16.png"
|
||||
sizes="16x16"
|
||||
/>
|
||||
|
||||
<link
|
||||
rel="mask-icon"
|
||||
href="{{ site.baseurl }}/images/safari-pinned-tab.svg"
|
||||
color="#5bbad5"
|
||||
/>
|
||||
|
||||
{% seo %} {% feed_meta %}
|
||||
|
||||
<!-- Normalize -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.min.css"
|
||||
/>
|
||||
|
||||
<!-- Share -->
|
||||
<script
|
||||
defer
|
||||
src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.8"
|
||||
id="facebook-jssdk"
|
||||
></script>
|
||||
<script
|
||||
defer
|
||||
src="https://platform.twitter.com/widgets.js"
|
||||
id="twitter-wjs"
|
||||
></script>
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" href="{{ site.baseurl }}/css/index.css" />
|
||||
</head>
|
@ -1,9 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
<section>
|
||||
<article class="article">
|
||||
<div class="article__content">{{ content }}</div>
|
||||
</article>
|
||||
</section>
|
@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{% include head.html %}
|
||||
|
||||
<body>
|
||||
{% include header.html %}
|
||||
{{ content }}
|
||||
{% include footer.html %}
|
||||
</body>
|
||||
</html>
|
@ -1,53 +0,0 @@
|
||||
---
|
||||
permalink: "/"
|
||||
layout:
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{% include head.html %}
|
||||
<body>
|
||||
<div class="metro">
|
||||
<h1 class="logo">
|
||||
<img src="{{ site.baseurl }}/images/logo.svg" style="height: 50px" />
|
||||
</h1>
|
||||
<nav>
|
||||
<ul class="menu">
|
||||
<li class="menu__item">
|
||||
<a href="/me">Me</a>
|
||||
</li>
|
||||
<li class="menu__item">
|
||||
<a href="https://github.com/{{ site.github.username }}">GitHub</a>
|
||||
</li>
|
||||
<li class="menu__item">
|
||||
<a href="https://twitter.com/{{ site.twitter.username }}"
|
||||
>Twitter</a
|
||||
>
|
||||
</li>
|
||||
<li class="menu__item">
|
||||
<a href="/wallpaper">Wallpaper</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<section>
|
||||
{% for post in site.posts %}
|
||||
<article class="article-list__item">
|
||||
<h2 class="article-list__item__title">
|
||||
<a href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a>
|
||||
</h2>
|
||||
<!-- <time class="article-list__item__pubdate" pubdate>{{ post.date | date: '%B %d, %Y' }}</time> -->
|
||||
<article class="article-list__item__excerpt">
|
||||
{{ post.excerpt | strip_html | truncate: 140, "..." }}
|
||||
</article>
|
||||
<a
|
||||
class="article-list__item__button"
|
||||
href="{{ post.url | prepend: site.baseurl }}"
|
||||
>Read more</a
|
||||
>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% include footer.html %}
|
||||
</body>
|
||||
</html>
|
@ -1,48 +0,0 @@
|
||||
---
|
||||
title: Wallpapers
|
||||
permalink: "/wallpaper"
|
||||
layout: post
|
||||
---
|
||||
|
||||
Right-click and choose **Save Image** to download my wallpapers. All following pictures are my own work and are published on Creative Commons BY-NC-SA.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
<div style="display: flex; flex-direction: row">
|
||||
<div><img src="{{ site.baseurl }}/images/wallpaper/vertex.png" /></div>
|
||||
<div style="margin-left: 20px"><img src="{{ site.baseurl }}/images/wallpaper/vertex2.png" /></div>
|
||||
</div>
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Monochrome
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
37
package.json
@ -1,19 +1,30 @@
|
||||
{
|
||||
"name": "uechi.io",
|
||||
"name": "hexo-site",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"fix-typo": "textlint _posts/*.md _pages/*.md --fix --dry-run -f diff",
|
||||
"build": "bundle exec jekyll build",
|
||||
"start": "bundle exec jekyll serve --host 0.0.0.0 --port 4000 --baseurl '' --force_polling --drafts --unpublished --incremental",
|
||||
"test": "textlint _posts/*.md _pages/*.md -f pretty-error"
|
||||
"build": "hexo generate",
|
||||
"clean": "hexo clean",
|
||||
"deploy": "hexo deploy",
|
||||
"start": "yarn clean && hexo server --debug"
|
||||
},
|
||||
"devDependencies": {
|
||||
"textlint": "^11.7.6",
|
||||
"textlint-filter-rule-whitelist": "^2.0.0",
|
||||
"textlint-rule-common-misspellings": "^1.0.1",
|
||||
"textlint-rule-max-ten": "^2.0.4",
|
||||
"textlint-rule-no-dropping-the-ra": "^1.1.3",
|
||||
"textlint-rule-no-start-duplicated-conjunction": "^2.0.2",
|
||||
"textlint-rule-preset-japanese": "^5.0.0"
|
||||
"dependencies": {
|
||||
"hexo": "^5.0.0",
|
||||
"hexo-excerpt": "^1.1.6",
|
||||
"hexo-filter-mathjax": "^0.6.3",
|
||||
"hexo-generator-archive": "^1.0.0",
|
||||
"hexo-generator-category": "^1.0.0",
|
||||
"hexo-generator-feed": "^3.0.0",
|
||||
"hexo-generator-index": "^2.0.0",
|
||||
"hexo-generator-tag": "^1.0.0",
|
||||
"hexo-renderer-ejs": "^1.0.0",
|
||||
"hexo-renderer-pandoc": "^0.3.0",
|
||||
"hexo-renderer-sass": "^0.4.0",
|
||||
"hexo-renderer-stylus": "^2.0.0",
|
||||
"hexo-server": "^2.0.0",
|
||||
"hexo-theme-landscape": "^0.0.3"
|
||||
},
|
||||
"hexo": {
|
||||
"version": "5.3.0"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
4
scaffolds/draft.md
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: {{ title }}
|
||||
tags:
|
||||
---
|
3
scaffolds/page.md
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
title: {{ title }}
|
||||
---
|
5
scaffolds/post.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
title: {{ title }}
|
||||
date: {{ date }}
|
||||
tags:
|
||||
---
|
@ -1,12 +1,11 @@
|
||||
---
|
||||
title: Page Not Found
|
||||
permalink: "/404.html"
|
||||
excerpt: ''
|
||||
permalink: "/404"
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{% include head.html %}
|
||||
<%- partial('head.html') %>
|
||||
<style>
|
||||
h1 {
|
||||
font-size: 12em;
|
||||
@ -15,9 +14,9 @@ excerpt: ''
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
{% include header.html %}
|
||||
<section class='page-section error-page'>
|
||||
<div class='container-narrow text-center py-md-8'>
|
||||
<%- partial('header.html') %>
|
||||
<section class="page-section error-page">
|
||||
<div class="container-narrow text-center py-md-8">
|
||||
<h1>404</h1>
|
||||
<p class="lead">This page could not be found</p>
|
||||
</div>
|
45
source/_drafts/affinity-thumbnail.md
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
title: Extract Thumbnail Image from Affinity Photo and Affinity Design
|
||||
---
|
||||
|
||||
Nextcloud doesn't have a support for thumbnail generation from Affinity Photo and Affinity Design. So I had to do it myself.
|
||||
|
||||
# Digging Binary
|
||||
|
||||
Glancing at `.afphoto` and `.afdesign` in Finder, I noticed that it has a QuickLook support and an ability to show the thumbnail image. So these files should have thumbnail image somewhere inside its binary.
|
||||
|
||||
I wrote a simple script to seek for thumbnail image from a binary and save it as `.png` file.
|
||||
|
||||
```js af.js
|
||||
const fs = require("fs");
|
||||
|
||||
// png spec: https://www.w3.org/TR/PNG/
|
||||
const PNG_SIG = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
||||
const IEND_SIG = Buffer.from([73, 69, 78, 68]);
|
||||
|
||||
function extractThumbnail(buf) {
|
||||
const start = buf.indexOf(PNG_SIG);
|
||||
const end = buf.indexOf(IEND_SIG, start) + IEND_SIG.length * 2; // IEND + CRC
|
||||
return buf.subarray(start, end);
|
||||
}
|
||||
|
||||
function generateThumbnail(input, output) {
|
||||
const buf = fs.readFileSync(input);
|
||||
const thumbBuf = extractThumbnail(buf);
|
||||
fs.writeFileSync(output, thumbBuf);
|
||||
}
|
||||
|
||||
generateThumbnail(process.argv[2], process.argv[3] || "output.png");
|
||||
```
|
||||
|
||||
That's right. This script just scrapes a binary file and extracts the portion of which starts with `PNG` signature and ends with `IEND`.
|
||||
|
||||
Now I can generate a thumbnail image from arbitrary `.afphoto` and `.afdesign` file. Let's move on delving into Nextcloud source code.
|
||||
|
||||
# Tweaking Nextcloud
|
||||
|
||||
I have a little experience in tweaking Nextcloud source code before, where I implemented thumbnail generator for PDFs, so it should be easier this time, hopefully.
|
||||
|
||||

|
||||
|
||||
Anyway, long story short, I got Nextcloud generates thumbnail images for Affinity files by implementing PreviewGenerator class.
|
BIN
source/_drafts/affinity-thumbnail/afphoto.png
Normal file
After Width: | Height: | Size: 437 KiB |
36
source/_drafts/building-chromium.md
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
title: Build Chromium from zero
|
||||
---
|
||||
|
||||
事前準備する。
|
||||
|
||||
```
|
||||
brew install cache
|
||||
git config --global core.precomposeUnicode true
|
||||
```
|
||||
|
||||
ソースコードを手に入れる。
|
||||
|
||||
```shell
|
||||
ghq get https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
cd `ghq root`/chromium.googlesource.com/chromium
|
||||
fetch chromium
|
||||
```
|
||||
|
||||
`.envrc` に以下を追加し、`direnv allow`で環境変数を適用する。
|
||||
|
||||
```shell
|
||||
PATH_add `qhq root`/chromium.googlesource.com/chromium/tools/depot_tools
|
||||
PATH_add src/third_party/llvm-build/Release+Asserts/bin
|
||||
export CCACHE_CPP2=yes
|
||||
export CCACHE_SLOPPINESS=time_macros
|
||||
export SPACESHIP_GIT_SHOW=false
|
||||
```
|
||||
|
||||
ビルドする。
|
||||
|
||||
```shell
|
||||
cd src
|
||||
gn gen out/Default --args='cc_wrapper="ccache"'
|
||||
autoninja -C out/Default chrome
|
||||
```
|
181
source/_drafts/camping.md
Normal file
@ -0,0 +1,181 @@
|
||||
---
|
||||
title: 準ソロキャンプ
|
||||
---
|
||||
|
||||
半ブッシュクラフト + バックパックで運べる重量 + 雰囲気重視
|
||||
|
||||
# 道具一覧
|
||||
|
||||
- テント
|
||||
- MSR Elixir 1 (Green)
|
||||
- 標準の白色は虫が集まりやすいためグリーンにした
|
||||
- 予算があるなら Hubba Hubba NX の方が絶対に良い
|
||||
- 寝袋
|
||||
- モンベル ダウンハガー 800 #3
|
||||
- 5 度を下回ると普通に寝れない
|
||||
- と思っていたが、サーモライトリアクターをインナーに入れることで氷点下 2 度の中で寝ることができた
|
||||
- マット
|
||||
- Thermarest Z Lite SOL S
|
||||
- 帆布シートの上に敷いて座布団としても使える
|
||||
- 敷物
|
||||
- asobito 綿帆布シート
|
||||
- そのままだと硬く寒いため銀マットを座布団として使う
|
||||
- 多少の防水性と耐火性があるため、焚き火・ブッシュクラフトスタイルと相性が良い
|
||||
- 前室に敷いておけば、寝ている間地面の湿気から荷物を守れる
|
||||
- 手袋
|
||||
- グリップスワニー G-1 ブラック
|
||||
- 燃えている薪を数秒程度なら持てる
|
||||
- 柔軟性があるため細かい作業ができる
|
||||
- ナイフ
|
||||
- MOSSYOAK 225mm
|
||||
- 薪割り、木工、着火、料理あらゆる場面で使える
|
||||
- 木の枝を加工して様々な道具を作れる
|
||||
- ペグ
|
||||
- 箸・スプーン・フォーク
|
||||
- ヘラ
|
||||
- 串
|
||||
- フック
|
||||
- トライポッド
|
||||
- Y 字スタンド(肉を回転させながら焼くやつ)
|
||||
- 蚊取り線香の台
|
||||
- 加工時に発生する木くずは火口として使える
|
||||
- 状況によってはサブナイフだけでも良い
|
||||
- サブナイフ
|
||||
- Victorinox
|
||||
- ワインオープナーと小型ナイフがメイン用途
|
||||
- ランタン
|
||||
- Feuerhand 276
|
||||
- こだわりが無ければ LED ランタンの方が圧倒的に明るいし便利
|
||||
- サブランタン
|
||||
- UCO キャンドルランタン オイル化改造
|
||||
- ソロキャンであればこれくらいの明るさでも作業できる
|
||||
- 2 人キャンで 2 人分をカバーするようなことはできない
|
||||
- 焚き火調理中にフライパンの中の様子を見たいといった場合には厳しいかもしれない
|
||||
- 不安定な地面やメッシュテーブルに置くと倒れそうになるのでパーツを自作して補強する必要がある
|
||||
- ヘッドライト
|
||||
- Petzl Zipka
|
||||
- 普通の作業でずり落ちることはない
|
||||
- 赤色灯が地味に便利で、目の明順応を軽減する効果がある
|
||||
- ゴミ袋
|
||||
- ジップロック
|
||||
- 基本戦略としてゴミを作らないことが大事野菜などは紙で包んでいくことで焚き火の火口にできる
|
||||
- iPhone
|
||||
- ロープワークのリファレンスアプリを入れてオフラインでも参照できるようにする
|
||||
- 現地までの道案内
|
||||
- 緊急連絡用
|
||||
- ポータブルバッテリー(ケーブル内蔵型)
|
||||
- iPhone の充電
|
||||
- ヘッドライトの充電
|
||||
- パラコード
|
||||
- テントやタープの固定、トライポッド自作、ケトルの吊り下げ、ランタンの吊り下げ、薪の固定等、あらゆる状況で使える
|
||||
- 1m や 3m のロープを何束か作っておくと便利
|
||||
- ジップロック L サイズ
|
||||
- 消毒用アルコール
|
||||
- トイレットペーパー
|
||||
|
||||
## 焚き火
|
||||
|
||||
- 斧
|
||||
- ハスクバーナのキャンプ用斧ダイヤモンドシャープナーで研いでおく
|
||||
- ハンマーとしても使える
|
||||
- 折りたたみノコギリ
|
||||
- 薪を現地調達するなら必要
|
||||
- フェロセリウムロッド
|
||||
- 趣味
|
||||
- マグネシウムロッドよりも火花が大きい大きいサイズが良い
|
||||
- マッチ
|
||||
- キャンドルランタンや焚き火の火口への点火時に使用する
|
||||
- 切ったロープの端を処理する際にも使える
|
||||
- ガストーチ(予備)
|
||||
- SOTO のマイクロトーチ
|
||||
- マッチが濡れた場合や雨天時に備えて用意しておく
|
||||
- 着火剤(予備)
|
||||
- ピコグリル 398(直火不可能な場合)
|
||||
- 網を乗せればバーベキューもできる
|
||||
|
||||
## 料理
|
||||
|
||||
- フライパン
|
||||
- Turk クラシック 24 cm
|
||||
- ケトル
|
||||
- ベルモント アルミケトル 1.2L
|
||||
- 広口なので お湯を沸かす以外にも、パスタを茹でたりスープを作ったりして片手鍋のように扱える
|
||||
- ゴムパーツは焚き火で燃えてしまうのでカッターで除去済み
|
||||
- ステンレスマグ
|
||||
- チタン製はお茶の味が損なわれる上に軽すぎて倒れる危険があるため使っていない
|
||||
- テトラドリップ
|
||||
- コンパクトなドリッパー
|
||||
- コーヒー豆は出発前に挽いておくと楽
|
||||
- Victorinox ウェイター
|
||||
- ワインや瓶ビールを開けたり、野菜を切るときに使う
|
||||
- まな板
|
||||
- MSR の折りたたみまな板 V 字型に折り曲がるため切った材料をフライパンに入れやすい
|
||||
- 木の板でもなんでも好きなもの使えば良いと思う
|
||||
- 熱いケトルが置けるため最近は木のまな板を使っている
|
||||
- ウォーターパック
|
||||
- エバニュー ウォーターキャリー 2 L(飲料水用)
|
||||
- モンベルウォーターバッグ 2L(川水用)
|
||||
- Sawyer Mini 浄水器
|
||||
- お皿
|
||||
- 状況に合わせてシェラカップか大きめのステンレス皿を選ぶ
|
||||
- フォーク
|
||||
- 木製のフォーク
|
||||
- 叉の間隔が広いタイプであれば指だけで洗えるため便利
|
||||
- ケトルに入れっぱなしにしても熱くならない
|
||||
- 金属製品を傷つけない
|
||||
- スプーン
|
||||
- 木製のスプーン
|
||||
- 主に調理用
|
||||
- 金属たわし
|
||||
- 洗剤ミニボトル(50ml)
|
||||
- 環境にやさしいタイプが良い
|
||||
- 泡ポンプタイプが楽
|
||||
- オリーブオイルボトル
|
||||
- パラフィンシートで蓋を覆っておけば液漏れの心配が無い
|
||||
- あるいは詰替え用のポンプ or スプレーボトルに入れておく
|
||||
- 塩・粗挽き黒コショウボトル
|
||||
- 最低限の味付けができる後はやりたい料理によってバジルやパセリ、タイム、パプリカ等を持っていく
|
||||
|
||||
## ボディケア
|
||||
|
||||
- ボディケアシート
|
||||
- 寝る前と起きた後に身体を拭く
|
||||
- ズボラボのシートで顔を拭けば皮脂も気にならない
|
||||
- 虫除けスプレー
|
||||
- ハッカ油系のスプレー
|
||||
- 目に噴射しないように注意する
|
||||
- 液体歯磨き
|
||||
- 耳栓
|
||||
- フィットさえすれば完全な静寂を得られるため、虫の羽音や他のキャンパーの騒音を心配することがなくなる
|
||||
- Moldex の中でも Purafit が一番防音性と装着感が高いが、個人差があるため一度全種類試した方が良い
|
||||
- 絆創膏
|
||||
|
||||
## 着替え
|
||||
|
||||
- ビーニー(秋冬)
|
||||
- 就寝用の虫刺され対策にもなる
|
||||
- ネックウォーマーまたはマフラー(冬)
|
||||
- 首周りの防寒は大事
|
||||
- フリースジャケット(冬)
|
||||
- シェルジャケット(秋冬)
|
||||
- 寝袋の上に被せることで暖かさを調整できる
|
||||
- 枕にもなる
|
||||
- シャツ
|
||||
- ヘインズ or ヒートテック
|
||||
- トップス・ボトムスは使い回す
|
||||
- 下着
|
||||
- タオル
|
||||
- 防水サック
|
||||
- 着替えを入れて枕にできる
|
||||
- コンタクトレンズ
|
||||
- メガネ
|
||||
- 夜トイレに行く場合に必要
|
||||
|
||||
# 気分で持っていく道具
|
||||
|
||||
- テーブル
|
||||
- 椅子
|
||||
- フィールドホッパー
|
||||
- ホットサンドメーカー
|
||||
- シングルバーナー
|
||||
- コッヘル
|
23
source/_drafts/china-travel-trips.md
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: 深圳を旅する Tips
|
||||
---
|
||||
|
||||
## WeChat Pay
|
||||
|
||||
深圳での食事は殆ど WeChat Pay で支払うことが出来た。
|
||||
|
||||
## UnionPay
|
||||
|
||||
ホテルやスターバックスで Visa カードを使うことが出来なかったため、UnionPay カードで支払うことになった。
|
||||
|
||||
## Pocketchange
|
||||
|
||||
日本の空港に設置している pocketchange で外貨や日本円を各種電子マネーに両替することが出来る。
|
||||
|
||||
## Google Translate
|
||||
|
||||
翻訳データはダウンロードしてローカルで使えるようにしておくこと。
|
||||
|
||||
## Octopus / 深圳通
|
||||
|
||||
KKday で事前予約していた物を香港空港で受け取った。深圳通は現地で購入した。
|
30
source/_drafts/developing-web-apps.md
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
title: Developing Web Apps in One Minutes
|
||||
---
|
||||
|
||||
## 0. Setup Homebrew and Node
|
||||
|
||||
```
|
||||
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
```
|
||||
|
||||
```
|
||||
brew install node
|
||||
```
|
||||
|
||||
## 1. Scaffold from template
|
||||
|
||||
```
|
||||
npx express-generator --view=pug awesome-app
|
||||
cd awesome-app
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
## 2. Deploy with Now
|
||||
|
||||
```
|
||||
npm install -g now
|
||||
now login
|
||||
now --public
|
||||
```
|
52
source/_drafts/math-api-ja.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
title: 数学API:SVG画像としてのLaTeX Math
|
||||
description: <img>のような場所にLaTeX数学方程式を配置します。
|
||||
tags:
|
||||
- LaTeX
|
||||
- Math
|
||||
- API
|
||||
- showdev
|
||||
---
|
||||
|
||||
私はいつも、LaTeX Math 方程式を、MathJax が内部で実行できない Web ページに配置したいと思っていました。
|
||||
|
||||
少し時間をかけて、LaTeX Math マークアップを SVG 画像にレンダリングする[Math API](https://math.now.sh) を作成しました。
|
||||
|
||||
したがって、GitHub、Jupyter Notebook、dev.to、そして Qiita(こちら!)など、 `<img>`または Markdown( `![]()`)を配置できるほぼすべての場所に方程式を配置できます。
|
||||
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||

|
||||
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
$$
|
||||
\log\prod^N_i x_i = \sum^N_i \log{x_i}
|
||||
$$
|
||||
|
||||
## インライン画像
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
クエリを「from」から「inline」に変更することにより、インライン方程式を生成することができます。
|
||||
|
||||
```markdown
|
||||
<img src="https://math.now.sh?inline=\\LaTeX" />
|
||||
```
|
||||
|
||||
## オンラインエディター
|
||||
|
||||
また、[Math API](https://math.now.sh) で利用可能なオンラインエディターがあります。
|
||||
|
||||

|
||||
|
||||
## 結論
|
||||
|
||||
ソースコードは[GitHub](https://github.com/uetchy/math-api)で入手できます。
|
||||
それを試してみて、新機能のコメント/アイデアを残してください。
|
63
source/_drafts/namae-ja.md
Normal file
@ -0,0 +1,63 @@
|
||||
---
|
||||
title: namae.devでアプリのスリック名を付けます
|
||||
tags:
|
||||
- javascript
|
||||
- web
|
||||
cover_image: https://thepracticaldev.s3.amazonaws.com/i/uafydwlnfneikuiyxe2w.png
|
||||
---
|
||||
|
||||
新しい OSS プロジェクトまたは Web アプリの命名に苦労したことがありますか? GitHub、npm、Homebrew、PyPI、Domains などで希望するものを誰も要求していないことを望みながら、最適な名前を 選択するのは退屈です。
|
||||
|
||||
だからこそ、[namae](https://namae.dev)を作成しました。
|
||||
|
||||
## namae
|
||||
|
||||

|
||||
|
||||
[namae](https://namae.dev) は、開発者と起業家向けのプラットフォーム間名前可用性チェッカーです。
|
||||
|
||||
使用する名前をフォームに入力すると、namae はさまざまなレジストリを調べて、名前がすでに使用されているかどうかを確認します。
|
||||
|
||||

|
||||
|
||||
## サポートされているプラ ットフォーム
|
||||
|
||||
namae は 15 のパッケージレジストリと Web プラットフォームをサポートしており、成長しています。
|
||||
|
||||
- ドメイン
|
||||
- GitHub Organization
|
||||
- npm / npm Organization
|
||||
- PyPI
|
||||
- RubyGems
|
||||
- crates.io (Rust)
|
||||
- Homebrew / Homebrew Cask
|
||||
- LaunchPad / APT (Linux)
|
||||
- Twitter
|
||||
- Spectrum
|
||||
- Slack
|
||||
- Heroku
|
||||
- ZEIT Now
|
||||
- AWS S3
|
||||
- js.org
|
||||
|
||||
さらに、検索結果には、**GitHub**および**App Store**に類似した名前のプロジェクトのリストが含まれています。
|
||||
|
||||
## 名前の提案
|
||||
|
||||
namae には、**Name Suggestion**という独自の機能もあります。共通の接頭辞/接尾辞と同義語で構成される自動生成された名前を提案します。いくつかの例を見てみましょう。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
提案をクリックすると、ナマエはフォームを完成させて、レジストリを検索し始めます。
|
||||
|
||||
## オープンソース
|
||||
|
||||
namae は完全にオープンソースであり、ソースコード全体は[GitHub](https://github.com/uetchy/namae)で入手できます。 API 用の Node.js Lambda と Web フロントエンド用の React アプリで構成され、[ZEIT Now](https://now.sh)で実行されています。
|
||||
|
||||
## 結論
|
||||
|
||||
namae を使用すると、ホスティングプロバイダーとパッケージレジストリのセットの周りで普遍的に利用可能な名前を検索する時間を節約できます。
|
||||
|
||||
[namae.dev](https://namae.dev/)に移動して、将来の製品名が入手可能かどうかのレポートを取得します。何か提案があれば、コメントを残すか、Twitter([@uetschy](https://twitter.com/uetschy))で私に連絡してください。
|
21
source/_drafts/node-postgresql-comparion.md
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
title: Node.js ORM Comparison
|
||||
---
|
||||
|
||||
ORM いろいろあるね。
|
||||
|
||||
- [prisma/prisma](https://github.com/prisma/prisma) - Modern database access (ORM alternative) for Node.js & TypeScript
|
||||
- PostgreSQL, MySQL, SQLite
|
||||
- [mikro-orm/mikro-orm](https://github.com/mikro-orm/mikro-orm) - TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns.
|
||||
- MongoDB, MySQL, MariaDB, PostgreSQL, SQLite
|
||||
- [typeorm/typeorm](https://github.com/typeorm/typeorm) - ORM for TypeScript and JavaScript
|
||||
- MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, SAP Hana, WebSQL
|
||||
- NodeJS, Browser, Ionic, Cordova and Electron
|
||||
- [sequelize/sequelize](https://github.com/sequelize/sequelize) - An easy-to-use multi SQL dialect ORM for Node.js
|
||||
|
||||
- Postgres, MySQL, MariaDB, SQLite, Microsoft SQL Server.
|
||||
|
||||
- [Vincit/objection.js](https://github.com/Vincit/objection.js) - An SQL-friendly ORM for Node.js built on knex.
|
||||
- PostgreSQL, MySQL, SQLite3
|
||||
- [knex/knex](https://github.com/knex/knex) - A query builder designed to be flexible, portable, and fun to use.
|
||||
- PostgreSQL, MySQL, SQLite3
|
29
source/_drafts/packet-capturing.md
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
title: パケットキャプチャリング
|
||||
---
|
||||
|
||||
- macOS Mojave では、Wi-Fi をオフにしていないと Monitor モードでスキャンしてもパケットが受信できない。
|
||||
- Preferences > Protocols > IEEE 802.11 で Decrypt Keys を保存する。wpa-psk でハッシュ化された値を保存したほうが安全である。保存先は`.config/wireshark`
|
||||
- 暗号化された 802.11 通信を覗くには 4-ways handshake (EAPOL)を観測する必要がある。そのためには対象デバイスの Wi-Fi をトグルすれば良い。
|
||||
|
||||
## コマンド
|
||||
|
||||
```
|
||||
tcpdump -i en0 -I
|
||||
```
|
||||
|
||||
で tcp ダンプ
|
||||
|
||||
```
|
||||
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I
|
||||
```
|
||||
|
||||
で現在接続しているネットワークの情報を取得
|
||||
|
||||
# Charles
|
||||
|
||||
1. Charles で`Proxy > Proxy Settings`を開き、HTTP Proxy の Port を選ぶ。
|
||||
2. Enable transparet HTTP proxying にチェックする
|
||||
3. Charles 上で`Help > SSL Proxying > Install Charles Root Certificate on a Mobile Device or Remote Browsers`をクリックし、表示されているアドレスとポートを iOS の`Settings > Wi-Fi > 任意のAP > Proxy`に入力する。
|
||||
4. iOS で`chls.pro/ssl`にアクセスし、プロファイルをインストール
|
||||
5. `Settings > General > About > Certificate Trust Settings`で Charles の証明書を信用する。
|
21
source/_drafts/parseint-magic.md
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
title: "[].map(parseInt)"
|
||||
---
|
||||
|
||||
## Fan fact
|
||||
|
||||
`[0xa, 0xa, 0xa].map(parseInt)` results in `[10, NaN, 2]`.
|
||||
|
||||
## Why???
|
||||
|
||||
`parseInt(0xa, 0, [0xa, 0xa, 0xa])`
|
||||
|
||||
The second argument is `0` so the first argument gonna be treated as decimal number becoming `10`.
|
||||
|
||||
`parseInt(0xa, 1, [0xa, 0xa, 0xa])`
|
||||
|
||||
The second argument is `1` which is invalid as a radix so the result ends up with `NaN`.
|
||||
|
||||
`parseInt(0xa, 2, [0xa, 0xa, 0xa])`
|
||||
|
||||
The second argument is `2` meaning the first argument going to be handled as a binary number. `0xa` is `10` in binary, which results in `2` in decimal form.
|
79
source/_drafts/rsa-note.md
Normal file
@ -0,0 +1,79 @@
|
||||
---
|
||||
title: RSA
|
||||
---
|
||||
|
||||
http://inaz2.hatenablog.com/entry/2013/11/27/225953
|
||||
|
||||
```
|
||||
openssl genrsa 32 > key.pem
|
||||
openssl rsa -text < key.pem
|
||||
```
|
||||
|
||||
```
|
||||
modulus: 2608173289 (0x9b7590e9)
|
||||
publicExponent: 65537 (0x10001)
|
||||
privateExponent: 1888610089 (0x7091e729)
|
||||
prime1: 52223 (0xcbff)
|
||||
prime2: 49943 (0xc317)
|
||||
exponent1: 1459 (0x5b3)
|
||||
exponent2: 3417 (0xd59)
|
||||
coefficient: 17568 (0x44a0)
|
||||
```
|
||||
|
||||
$$\text{modulus} = \text{prime1} \cdot \text{prime2}$$
|
||||
|
||||
publicExponent は $(\text{prime1} - 1)(\text{prime2} - 1)$ とお互いに素な数から選ぶ。65537 で固定、なぜなら二進数で 10000000000000001 となり、ビットがあまり立っておらず計算が早いため。
|
||||
|
||||
privateExponent は $\text{publicExponent}^{-1} \text{mod} (\text{prime1} - 1)(\text{prime2} - 1)$
|
||||
|
||||
## 中国の余剰定理
|
||||
|
||||
[定理の詳細](https://ja.wikipedia.org/wiki/中国の剰余定理)
|
||||
|
||||
$$\text{exponent1} = \text{privateExponent} \pmod{\text{prime1} - 1}$$
|
||||
|
||||
$\text{exponent2} = \text{privateExponent} \pmod{\text{prime2} - 1} $
|
||||
|
||||
$ \text{coefficient} = \text{prime2}^{-1} \pmod{\text{prime1}} $
|
||||
|
||||
これらは復号の簡単化のために用意された係数である。
|
||||
|
||||
## 公開鍵の中身
|
||||
|
||||
```
|
||||
openssl rsa -pubout < key.pem > pub.pem
|
||||
openssl rsa -text -pubin < pub.pem
|
||||
```
|
||||
|
||||
```
|
||||
Modulus: 2608173289 (0x9b7590e9)
|
||||
Exponent: 65537 (0x10001)
|
||||
```
|
||||
|
||||
## 暗号
|
||||
|
||||
$ \text{source}^\text{publicExponent} \pmod{\text{modulus}} = \text{encryptedText} $
|
||||
|
||||
## 復号
|
||||
|
||||
$ \text{encryptedText}^\text{privateExponent} \mod \text{modulus} $
|
||||
|
||||
# Diffie-Helmann 鍵共有
|
||||
|
||||
## 一方向性関数
|
||||
|
||||
$ \mathrm{G}^x \mod \mathrm{P} = y $
|
||||
|
||||
右辺を求めるのは簡単だが、余り$y$から$x$を求めるのは難しい。
|
||||
|
||||
この性質を利用して、$x$に秘密情報を与えて交換することで第三者による復号を防げる。
|
||||
|
||||
A は$G^A \mod P$を B に送信
|
||||
→$(G^A \mod P)^B \mod P = (G^{A \cdot B}) \mod P$
|
||||
|
||||
B は$G^B \mod P$を A に送信
|
||||
→$ (G^B \mod P)^A \mod P = (G^{B \cdot A}) \mod P$
|
||||
|
||||
以下の法則を使用しているため、お互いに同一の結果を得られる。
|
||||
|
||||
$ (G^A)^B = G^{A \cdot B}$
|
14
source/_drafts/silence.md
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
date: 2020-02-13 16:22:05 +0900
|
||||
title: 静寂を得る方法
|
||||
---
|
||||
|
||||
聴覚過敏であったり、そうでなくとも周りの音がパフォーマンスに悪影響を与える人のために、静寂を得る方法を紹介します。
|
||||
|
||||
## EARIN M-2
|
||||
|
||||
[EARIN](https://earin.com/) は左右分離型 Bluetooth イヤホンです。付属のイヤホンの代わりに自分の耳にフィットする Comply のイヤーチップと付け替えます。
|
||||
|
||||
## Moldex
|
||||
|
||||
Moldex は使い捨て耳栓のメーカーであり、各種遮音レベルに分かれた多様なラインナップを提供しています。
|
40
source/_drafts/新しい自鯖.md
Normal file
@ -0,0 +1,40 @@
|
||||
---
|
||||
title: 新しい自鯖
|
||||
---
|
||||
|
||||
10年ぶりに新しいサーバーを調達しました。最後に自鯖を組んだ時は高校生、沖縄に住んでいた頃です。BTOのタワーPCにDebianを入れてWebサーバーにしていました。UPSとか無いので台風で停電するたびにWebサービスが落ちるヘボ感です。
|
||||
|
||||
今回も完成品を買ってしまえばそれでお終いですが折角ですし、なにより面白そうなのでパーツからサーバーを組み立てましょう。初めてのAMD、初めてのDDR4メモリ、初めてのNVM Expressです。
|
||||
|
||||
# スペック
|
||||
|
||||
用途を考えましょう。
|
||||
|
||||
- 機械学習サーバー
|
||||
- 自宅クラウド
|
||||
- メールサーバー
|
||||
- ファイルサーバー (Nextcloud)
|
||||
- VPNサーバー他
|
||||
- VS Code Remote SSHのホスト先
|
||||
- 重いmakeやらなんやら
|
||||
- TabNine
|
||||
- Webサーバー
|
||||
- WebアプリやTelegram botのデプロイ先
|
||||
|
||||
重いタスクを並列してやらせたいので最優先はCPUとメモリです。メモリはデュアルリンクを重視して32GBx2を、CPUは昨今のライブラリのマルチコア対応を勘案してRyzen 9 3950Xにしました。
|
||||
|
||||
> 結果から言うとメモリはもっと必要でした。巨大なPandasデータフレームを並列処理なんかするとサクッと消えてしまいます。予算に余裕があるなら128GBほど用意したほうが良いです。
|
||||
|
||||
GPUは古いサーバーに突っ込んでいたNVIDIA GeForce GTX TITAN X (Maxwell)を流用しました。メモリが12GBありますが、最大ワークロード時でも5GBは残るので今のところ十分です。
|
||||
|
||||
記憶装置は3TB HDD 2台と500GB NVMeメモリです。NVMeメモリはOS用、HDDはデータとバックアップ用です。
|
||||
|
||||
マザーボードはASRockのB550 Taichiです。X570マザーと比較して、実装されているコンデンサーや安定性でB550にしました。
|
||||
|
||||
今後GPUを追加することを考えて800W電源を選びました。実際にサーバーを稼働させて使用電力を計測してみると、アイドル時に180W前後、フル稼働時でも350Wを超えないくらいでした。今後UPSを買う場合はその付近+バッファのグレードを買うと良いかもしれません。
|
||||
|
||||
ケースはFractal DesignのMeshify 2にしました。シンプルで良い。
|
||||
|
||||
OSは長年親しんできたDebian系を卒業してArchlinuxにしてみました。すでにファンになりかけています。本当に何も用意してくれません。セットアップウィザードとかないです。いきなりシングルユーザーモードにぶち込まれます。`which`すらインストールしなければ使えません。潔癖症の自覚がある人はArchを使いましょう。あとAURにいくつかパッケージを公開してみたので、よければvoteお願いします。
|
||||
|
||||
Arch Linuxのセットアップは個別に記事を書いたので読んでください。入力したコマンドを全て記録したので再現性があります。
|
@ -1,7 +1,8 @@
|
||||
---
|
||||
title: 初キャンプに必要な機材
|
||||
date: 2018-04-13 14:26:00 +09:00
|
||||
redirect_from: "/blog/2018/04/13/camping"
|
||||
redirect_from:
|
||||
- "/blog/2018/04/13/camping"
|
||||
---
|
||||
|
||||
先月、大洗で初めてのキャンプ泊をした。
|
@ -3,11 +3,11 @@ title: Know your deps on package.json in seconds
|
||||
date: 2018-09-02 03:23:00 +09:00
|
||||
---
|
||||
|
||||

|
||||
|
||||
How do you know what packages that project/library depend on and what exactly are that packages doing for?
|
||||
You'll want to quickly survey on them. So [npm-deps-list](https://github.com/uetchy/npm-deps-list) is here for.
|
||||
|
||||

|
||||
|
||||
You can install them using `npm` or `yarn`.
|
||||
|
||||
```bash
|
@ -2,8 +2,10 @@
|
||||
title: 英語メモ
|
||||
date: 2019-01-17T10:31:00.000+00:00
|
||||
redirect_from: "/blog/2019/01/17/english-note"
|
||||
|
||||
---
|
||||
|
||||
雑多なメモです。
|
||||
|
||||
- spin up - 立ち上げる
|
||||
- woe - 悲しみ
|
||||
- a little too - 少し〜すぎる
|
||||
@ -94,8 +96,6 @@ redirect_from: "/blog/2019/01/17/english-note"
|
||||
|
||||
> It uses the definite article before "majority of the students", as if referring to a known group.
|
||||
|
||||
> Next piece is gonna be a super mega holy shit giantess 👍👍👍
|
||||
|
||||
> This sentence uses the indefinite article before the "majority of the students". This has the effect of introducing this group of students to the reader or the listener.
|
||||
|
||||
> It may give a hint that the exact number of the students that will vote is uncertain: it could be 51%, but then it could be 88%.
|
||||
@ -125,7 +125,7 @@ redirect_from: "/blog/2019/01/17/english-note"
|
||||
|
||||
> learning foreign languages is for me lifelong hobby. knowledge likely to requires much time to acquiring but it never decays.
|
||||
|
||||
> *sees client, whistles loudly*
|
||||
> _sees client, whistles loudly_
|
||||
|
||||
> weston comes out of nowhere running on all fours like a gorilla towards client
|
||||
|
@ -4,16 +4,18 @@ date: 2019-01-14 00:00:00 +09:00
|
||||
redirect_from: "/blog/2019/01/14/padsize"
|
||||
---
|
||||
|
||||
padStart における padSize の求め方です。
|
||||
|
||||
$$
|
||||
\textrm{padSize} = \lceil \log_{10}(\mathbf{arraySize} + 1) \rceil
|
||||
$$
|
||||
|
||||
```js
|
||||
const padSize = Math.ceil(Math.log10(arr.length + 1))
|
||||
const padSize = Math.ceil(Math.log10(arr.length + 1));
|
||||
|
||||
arr.forEach((item, index) => {
|
||||
console.log(`${index.padStart(padSize, '0')}: ${item}`)
|
||||
})
|
||||
console.log(`${index.padStart(padSize, "0")}: ${item}`);
|
||||
});
|
||||
```
|
||||
|
||||
結果は以下のようになる。
|
@ -21,14 +21,14 @@ electron-builder を利用して macOS 向け Electron アプリをコード署
|
||||
コード署名済みのアプリを[electron-notarize](https://github.com/electron-userland/electron-notarize)を使用して Apple Notary Service に提出します。
|
||||
|
||||
```js
|
||||
const { notarize } = require('electron-notarize')
|
||||
const { notarize } = require("electron-notarize");
|
||||
notarize({
|
||||
appBundleId,
|
||||
appPath,
|
||||
appleId,
|
||||
appleIdPassword,
|
||||
ascProvider,
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
- **appBundleId**: アプリの Bundle ID です。`package.json`の`build.appId`と同じものを使います。
|
||||
@ -44,33 +44,33 @@ electron-builder の afterSign フックを使用して、コード署名が済
|
||||
フックスクリプトを`./scripts/after-sign-mac.js`に置きます。
|
||||
|
||||
```js
|
||||
const path = require('path')
|
||||
const { notarize } = require('electron-notarize')
|
||||
const path = require("path");
|
||||
const { notarize } = require("electron-notarize");
|
||||
|
||||
const appleId = process.env.APPLE_ID
|
||||
const appleIdPassword = process.env.APPLE_PASSWORD
|
||||
const ascProvider = process.env.ASC_PROVIDER
|
||||
const appleId = process.env.APPLE_ID;
|
||||
const appleIdPassword = process.env.APPLE_PASSWORD;
|
||||
const ascProvider = process.env.ASC_PROVIDER;
|
||||
|
||||
const configPath = path.resolve(__dirname, '../package.json')
|
||||
const appPath = path.resolve(__dirname, '../dist/mac/App.app')
|
||||
const config = require(configPath)
|
||||
const appBundleId = config.build.appId
|
||||
const configPath = path.resolve(__dirname, "../package.json");
|
||||
const appPath = path.resolve(__dirname, "../dist/mac/App.app");
|
||||
const config = require(configPath);
|
||||
const appBundleId = config.build.appId;
|
||||
|
||||
async function notarizeApp() {
|
||||
console.log(`afterSign: Notarizing ${appBundleId} in ${appPath}`)
|
||||
console.log(`afterSign: Notarizing ${appBundleId} in ${appPath}`);
|
||||
await notarize({
|
||||
appBundleId,
|
||||
appPath,
|
||||
appleId,
|
||||
appleIdPassword,
|
||||
ascProvider,
|
||||
})
|
||||
console.log('afterSign: Notarized')
|
||||
});
|
||||
console.log("afterSign: Notarized");
|
||||
}
|
||||
|
||||
exports.default = async () => {
|
||||
await notarizeApp()
|
||||
}
|
||||
await notarizeApp();
|
||||
};
|
||||
```
|
||||
|
||||
`package.json`の`build`に`afterSign`を追加してコード署名が終わった後にスクリプトが実行されるようにします。
|
@ -7,9 +7,7 @@ date: 2019-10-03 17:21:00 +09:00
|
||||
|
||||
ベンチマーク用の TypeScript プログラムを用意します。
|
||||
|
||||
#### `a.ts`
|
||||
|
||||
```ts
|
||||
```ts a.ts
|
||||
function a() {
|
||||
const noise = Math.random() - 0.5;
|
||||
const offset = 1.0;
|
||||
@ -19,9 +17,7 @@ function a() {
|
||||
a();
|
||||
```
|
||||
|
||||
#### `b.ts`
|
||||
|
||||
```ts
|
||||
```ts b.ts
|
||||
function b() {
|
||||
const noise = Math.random() - 0.5;
|
||||
const offset = 2.0;
|
||||
@ -37,9 +33,9 @@ b();
|
||||
hyperfine 'ts-node a.ts' 'ts-node b.ts' -r 50 --warmup 3 --export-json ab.json
|
||||
```
|
||||
|
||||
`result.json`の中身以下のようになります。
|
||||
`result.json`の中身は以下のようになります。
|
||||
|
||||
```json
|
||||
```json result.json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
@ -87,8 +83,8 @@ hyperfine 'ts-node a.ts' 'ts-node b.ts' -r 50 --warmup 3 --export-json ab.json
|
||||
この`result.json`の`times`配列を受け取り、2 つの分布間に有意差があるかどうかを判定します。
|
||||
|
||||
```ts
|
||||
import fs from 'fs';
|
||||
import {jStat} from 'jstat';
|
||||
import fs from "fs";
|
||||
import { jStat } from "jstat";
|
||||
|
||||
const log = console.log;
|
||||
|
||||
@ -127,7 +123,7 @@ const Y = result.results[1].times;
|
||||
const p = ttest(X, Y);
|
||||
log(`p = ${p}`);
|
||||
log(`p < 0.05 = ${p < 0.05}`);
|
||||
log(p < 0.05 ? 'Possibly some difference there' : 'No difference');
|
||||
log(p < 0.05 ? "Possibly some difference there" : "No difference");
|
||||
```
|
||||
|
||||
ここで`X_mu`は分布 X の平均、`X_sigma`は分布 X の不偏分散です。
|
@ -1,8 +1,8 @@
|
||||
---
|
||||
title: Securing Local Dev Server
|
||||
date: 2020-02-07 00:00:00 +0900
|
||||
|
||||
---
|
||||
|
||||
Sometimes you want to interact with a local webserver with https support because of some browser APIs that are only available in an https environment.
|
||||
|
||||
You can easily create a self-signed TLS cert for development purposes with [`mkcert`](https://github.com/FiloSottile/mkcert).
|
600
source/_posts/2021/arch-linux-setup-guide.md
Normal file
@ -0,0 +1,600 @@
|
||||
---
|
||||
title: Arch Linux Setup Guide
|
||||
date: 2021-02-12
|
||||
---
|
||||
|
||||
This note includes all commands I typed when I setup Arch Linux on my new baremetal server.
|
||||
|
||||
# Why I choose Arch Linux
|
||||
|
||||
- Simple as it should be
|
||||
- Outstanding community efforts to maintaining package registry
|
||||
- Well organized wiki resources
|
||||
|
||||
# Useful links
|
||||
|
||||
- [General recommendations](https://wiki.archlinux.org/index.php/General_recommendations#Users_and_groups)
|
||||
- [System maintenance](https://wiki.archlinux.org/index.php/System_maintenance)
|
||||
- [Improving performance](https://wiki.archlinux.org/index.php/Improving_performance#Know_your_system)
|
||||
- [Benchmarking - ArchWiki](https://wiki.archlinux.org/index.php/Benchmarking)
|
||||
|
||||
# Provisioning
|
||||
|
||||
## wipe whole disk
|
||||
|
||||
```bash
|
||||
wipefs -a /dev/sda
|
||||
```
|
||||
|
||||
## create parition
|
||||
|
||||
```bash
|
||||
parted
|
||||
|
||||
select /dev/sda
|
||||
mktable gpt
|
||||
mkpart EFI fat32 0 512MB # EFI
|
||||
mkpart Arch ext4 512MB 100% # Arch
|
||||
set 1 esp on # flag part1 as ESP
|
||||
quit
|
||||
```
|
||||
|
||||
## install file-system
|
||||
|
||||
```bash
|
||||
mkfs.vfat -F 32 /dev/sda1 # EFI
|
||||
mkfs.ext4 /dev/sda2 # Arch
|
||||
```
|
||||
|
||||
## mount disk
|
||||
|
||||
```bash
|
||||
mkdir -p /mnt/boot
|
||||
mount /dev/sda2 /mnt
|
||||
mount /dev/sda1 /mnt/boot
|
||||
```
|
||||
|
||||
## install base & linux kernel
|
||||
|
||||
```bash
|
||||
reflector -f 10 --latest 30 --protocol https --sort rate --save /etc/pacman.d/mirrorlist # optimize mirror list
|
||||
|
||||
pacstrap /mnt base linux linux-firmware vim man-db man-pages git informant
|
||||
# base-devel need to be included as well?
|
||||
genfstab -U /mnt >> /mnt/etc/fstab
|
||||
arch-chroot /mnt
|
||||
```
|
||||
|
||||
```bash
|
||||
pacman -Syu # upgrade
|
||||
pacman -Qe # list explicitly installed pkgs
|
||||
pacman -Rs # remove pkg and its deps
|
||||
pacman -Qtd # list orphans
|
||||
```
|
||||
|
||||
## bootloader
|
||||
|
||||
```bash
|
||||
pacman -S \
|
||||
grub \
|
||||
efibootmgr \
|
||||
amd-ucode # AMD microcode
|
||||
grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB
|
||||
grub-mkconfig -o /boot/grub/grub.cfg
|
||||
```
|
||||
|
||||
## ntp
|
||||
|
||||
```bash
|
||||
sed -i -e 's/#NTP=/NTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org 2.arch.pool.ntp.org 3.arch.pool.ntp.org/' -e 's/#Fall/Fall/' /etc/systemd/timesyncd.conf
|
||||
systemctl enable --now systemd-timesyncd
|
||||
```
|
||||
|
||||
## locale
|
||||
|
||||
```bash
|
||||
ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
|
||||
hwclock --systohc
|
||||
vim /etc/locale.gen & locale-gen
|
||||
echo "LANG=en_US.UTF-8" > /etc/locale.conf
|
||||
```
|
||||
|
||||
## network
|
||||
|
||||
```bash
|
||||
hostnamectl set-hostname polka
|
||||
hostnamectl set-chassis server
|
||||
|
||||
vim /etc/hosts
|
||||
# 127.0.0.1 localhost
|
||||
# ::1 localhost
|
||||
# 127.0.0.1 polka
|
||||
```
|
||||
|
||||
See https://systemd.network/systemd.network.html.
|
||||
|
||||
```ini
|
||||
# /etc/systemd/network/wired.network
|
||||
[Match]
|
||||
Name=enp5s0
|
||||
|
||||
[Network]
|
||||
#DHCP=yes
|
||||
Address=10.0.1.2/24
|
||||
Gateway=10.0.1.1
|
||||
DNS=10.0.1.100 # self-hosted DNS resolver
|
||||
DNS=1.1.1.1 # Cloudflare for the fallback DNS server
|
||||
MACVLAN=dns-shim # to handle local dns lookup to 10.0.1.100 which is managed by Docker macvlan driver
|
||||
```
|
||||
|
||||
```ini
|
||||
# /etc/systemd/network/dns-shim.netdev
|
||||
# to handle local dns lookup to 10.0.1.100
|
||||
[NetDev]
|
||||
Name=dns-shim
|
||||
Kind=macvlan
|
||||
|
||||
[MACVLAN]
|
||||
Mode=bridge
|
||||
```
|
||||
|
||||
```ini
|
||||
# /etc/systemd/network/dns-shim.network
|
||||
# to handle local dns lookup to 10.0.1.100
|
||||
[Match]
|
||||
Name=dns-shim
|
||||
|
||||
[Network]
|
||||
IPForward=yes
|
||||
|
||||
[Address]
|
||||
Address=10.0.1.103/32
|
||||
Scope=link
|
||||
|
||||
[Route]
|
||||
Destination=10.0.1.100/30
|
||||
```
|
||||
|
||||
`ip` equivalent to the above settings:
|
||||
|
||||
```bash
|
||||
ip link add dns-shim link enp5s0 type macvlan mode bridge # add macvlan shim
|
||||
ip a add 10.0.1.103/32 dev dns-shim # assign host ip to shim defined in docker-compose.yml
|
||||
ip link set dns-shim up # enable interface
|
||||
ip route add 10.0.1.100/30 dev dns-shim # route macvlan subnet to shim interface
|
||||
```
|
||||
|
||||
```bash
|
||||
systemctl enable --now systemd-networkd
|
||||
networkctl status
|
||||
|
||||
# for self-hosted dns resolver
|
||||
sed -r -i -e 's/#?DNSStubListener=yes/DNSStubListener=no/g' -e 's/#DNS=/DNS=10.0.1.100/g' /etc/systemd/resolved.conf
|
||||
ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
|
||||
|
||||
systemctl enable --now systemd-resolved
|
||||
resolvectl status
|
||||
resolvectl query ddg.gg
|
||||
drill @10.0.1.100 ddg.gg
|
||||
|
||||
# FIXME
|
||||
pacman -S wpa_supplicant
|
||||
vim /etc/wpa_supplicant/wpa_supplicant.conf
|
||||
# ctrl_interface=/run/wpa_supplicant
|
||||
# update_config=1
|
||||
wpa_supplicant -B -i wlp8s0 -c /etc/wpa_supplicant/wpa_supplicant.conf
|
||||
wpa_cli # default control socket -> /var/run/wpa_supplicant
|
||||
modinfo iwlwifi
|
||||
```
|
||||
|
||||
If `networkctl` keep showing `enp5s0` as `degraded`, then run `ip addr add 10.0.1.2/24 dev enp5s0 ` to manually assign static IP address for the workaround.
|
||||
|
||||
## firewall
|
||||
|
||||
```bash
|
||||
pacman -S firewalld
|
||||
# TODO
|
||||
```
|
||||
|
||||
See also [Introduction to Netfilter – To Linux and beyond !](https://home.regit.org/netfilter-en/netfilter/)
|
||||
|
||||
## shell
|
||||
|
||||
```bash
|
||||
pacman -S zsh
|
||||
chsh -s /bin/zsh
|
||||
```
|
||||
|
||||
## user
|
||||
|
||||
```bash
|
||||
passwd # change root passwd
|
||||
|
||||
useradd -m -s /bin/zsh uetchy # add local user
|
||||
passwd uetchy # change local user password
|
||||
|
||||
userdbctl # verify users
|
||||
|
||||
pacman -S sudo
|
||||
echo "%sudo ALL=(ALL) NOPASSWD:/usr/bin/pacman" > /etc/sudoers.d/pacman # allow pacman without password
|
||||
usermod -aG sudo uetchy # add local user to sudo group
|
||||
```
|
||||
|
||||
## ssh
|
||||
|
||||
```bash
|
||||
pacman -S openssh
|
||||
vim /etc/ssh/sshd_config
|
||||
systemctl enable --now sshd
|
||||
```
|
||||
|
||||
on the host machine:
|
||||
|
||||
```bash
|
||||
ssh-copy-id uetchy@10.0.1.2
|
||||
```
|
||||
|
||||
## AUR
|
||||
|
||||
```bash
|
||||
git clone https://aur.archlinux.org/yay.git
|
||||
cd yay
|
||||
makepkg -si
|
||||
```
|
||||
|
||||
## finalize
|
||||
|
||||
```bash
|
||||
exit # leave chroot
|
||||
umount -R /mnt
|
||||
reboot
|
||||
```
|
||||
|
||||
# Additional setup
|
||||
|
||||
## gpgpu
|
||||
|
||||
```bash
|
||||
pacman -S nvidia
|
||||
cat /var/lib/modprobe.d/nvidia.conf # ensure having 'blacklist nouveau'
|
||||
|
||||
yay -S cuda-10.2 cudnn7-cuda10.2 # match the version number
|
||||
|
||||
nvidia-smi # test runtime
|
||||
```
|
||||
|
||||
## docker
|
||||
|
||||
```bash
|
||||
pacman -S docker docker-compose
|
||||
yay -S nvidia-container-runtime-bin
|
||||
vim /etc/docker/daemon.json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"log-driver": "journald",
|
||||
"log-opts": {
|
||||
"tag": "{{.ImageName}}/{{.Name}}/{{.ID}}"
|
||||
},
|
||||
"exec-opts": ["native.cgroupdriver=systemd"], // for kubernetes
|
||||
"runtimes": {
|
||||
// for docker-compose
|
||||
"nvidia": {
|
||||
"path": "/usr/bin/nvidia-container-runtime",
|
||||
"runtimeArgs": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
systemctl enable --now docker
|
||||
|
||||
groupadd docker
|
||||
usermod -aG docker user
|
||||
|
||||
docker run --rm -it --gpus all nvidia/cuda:10.2-cudnn7-runtime
|
||||
```
|
||||
|
||||
## telegraf
|
||||
|
||||
```bash
|
||||
yay -S telegraf
|
||||
vim /etc/telegraf/telegraf.conf
|
||||
```
|
||||
|
||||
```ini
|
||||
# File: /etc/sudoers.d/telegraf
|
||||
Cmnd_Alias FAIL2BAN = /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status *
|
||||
telegraf ALL=(root) NOEXEC: NOPASSWD: FAIL2BAN
|
||||
Defaults!FAIL2BAN !logfile, !syslog, !pam_session
|
||||
```
|
||||
|
||||
## fail2ban
|
||||
|
||||
```
|
||||
pacman -S fail2ban
|
||||
systemctl enable --now fail2ban
|
||||
```
|
||||
|
||||
```ini
|
||||
# File: /etc/fail2ban/jail.local
|
||||
[DEFAULT]
|
||||
bantime = 60m
|
||||
ignoreip = 127.0.0.1/8 10.0.1.0/24
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = 22,10122
|
||||
|
||||
[mailu]
|
||||
enabled = true
|
||||
backend = systemd
|
||||
journalmatch = CONTAINER_NAME=mailu_front_1
|
||||
port = smtp,submission
|
||||
chain = DOCKER-USER
|
||||
filter = mailu
|
||||
findtime = 600
|
||||
maxretry = 1
|
||||
bantime = 1d
|
||||
```
|
||||
|
||||
```ini
|
||||
# File: /etc/fail2ban/filter.d/mailu.conf
|
||||
[INCLUDES]
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
failregex = ^%(__prefix_line)s\d+\/\d+\/\d+ \d+:\d+:\d+ \[info\] \d+#\d+: \*\d+ client login failed: "Authentication credentials invalid" while in http auth state, client: <HOST>, server: \S+, login: "<F-USER>\S+</F-USER>"$
|
||||
ignoreregex =
|
||||
```
|
||||
|
||||
```
|
||||
fail2ban-client reload
|
||||
fail2ban-client status mailu
|
||||
```
|
||||
|
||||
## sendmail
|
||||
|
||||
```bash
|
||||
yay -S sendmail
|
||||
```
|
||||
|
||||
## cfddns
|
||||
|
||||
Dynamic DNS for Cloudflare.
|
||||
|
||||
```
|
||||
yay -S cfddns
|
||||
```
|
||||
|
||||
```yml
|
||||
# File: /etc/cfddns/cfddns.yml
|
||||
token: <token>
|
||||
```
|
||||
|
||||
```ini
|
||||
# File: /etc/cfddns/domains
|
||||
uechi.io
|
||||
datastore.uechi.io
|
||||
```
|
||||
|
||||
```
|
||||
systemctl enable --now cfddns
|
||||
```
|
||||
|
||||
## smart
|
||||
|
||||
```bash
|
||||
pacman -S smartmontools
|
||||
systemctl enable --now smartd
|
||||
```
|
||||
|
||||
## backup
|
||||
|
||||
```ini
|
||||
# File: /etc/backups/borg.service
|
||||
[Unit]
|
||||
Description=Borg Daily Backup Service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Nice=19
|
||||
IOSchedulingClass=2
|
||||
IOSchedulingPriority=7
|
||||
ExecStart=/etc/backups/run.sh
|
||||
```
|
||||
|
||||
```ini
|
||||
# File: /etc/backups/borg.timer
|
||||
[Unit]
|
||||
Description=Borg Daily Backup Timer
|
||||
|
||||
[Timer]
|
||||
WakeSystem=false
|
||||
OnCalendar=*-*-* 03:00
|
||||
RandomizedDelaySec=10min
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
```
|
||||
|
||||
```bash
|
||||
# File: /etc/backups/run.sh
|
||||
sleep 5
|
||||
|
||||
#
|
||||
# Script configuration
|
||||
#
|
||||
export BORG_PASSPHRASE="<PASSPHRASE>"
|
||||
MOUNTPOINT=/mnt/backup
|
||||
TARGET=$MOUNTPOINT/borg
|
||||
|
||||
# Archive name schema
|
||||
DATE=$(date --iso-8601)
|
||||
|
||||
# Options for borg create
|
||||
BORG_OPTS="--stats --compression lz4 --checkpoint-interval 86400"
|
||||
|
||||
# No one can answer if Borg asks these questions, it is better to just fail quickly
|
||||
# instead of hanging.
|
||||
export BORG_RELOCATED_REPO_ACCESS_IS_OK=no
|
||||
export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no
|
||||
|
||||
# Log Borg version
|
||||
borg --version
|
||||
|
||||
echo "Starting backup for $DATE"
|
||||
|
||||
echo "# system"
|
||||
borg create $BORG_OPTS \
|
||||
--exclude /root/.cache \
|
||||
--exclude /var/cache \
|
||||
--exclude /var/lib/docker/devicemapper \
|
||||
--exclude /home \
|
||||
--one-file-system \
|
||||
$TARGET::'{hostname}-system-{now}' \
|
||||
/ /boot
|
||||
|
||||
echo "# home"
|
||||
borg create $BORG_OPTS \
|
||||
--exclude 'sh:/home/*/.cache' \
|
||||
--exclude 'sh:/home/*/.cargo' \
|
||||
$TARGET::'{hostname}-home-{now}' \
|
||||
/home/
|
||||
|
||||
echo "# data"
|
||||
borg create $BORG_OPTS \
|
||||
$TARGET::'{hostname}-data-{now}' \
|
||||
/mnt/data
|
||||
|
||||
echo "Start pruning"
|
||||
BORG_PRUNE_OPTS="--list --stats --keep-daily 7 --keep-weekly 4 --keep-monthly 3"
|
||||
borg prune $BORG_PRUNE_OPTS --prefix '{hostname}-home-' $TARGET
|
||||
borg prune $BORG_PRUNE_OPTS --prefix '{hostname}-system-' $TARGET
|
||||
borg prune $BORG_PRUNE_OPTS --prefix '{hostname}-data-' $TARGET
|
||||
|
||||
echo "Completed backup for $DATE"
|
||||
|
||||
# Just to be completely paranoid
|
||||
sync
|
||||
```
|
||||
|
||||
```bash
|
||||
ln -sf /etc/backups/borg.* /etc/systemd/system/
|
||||
systemctl enable --now borg
|
||||
```
|
||||
|
||||
## kubernetes
|
||||
|
||||
```bash
|
||||
pacman -S kubeadm kubelet kubectl
|
||||
systemctl enable --now kubelet
|
||||
kubeadm init --pod-network-cidr='10.244.0.0/16'
|
||||
cp /etc/kubernetes/admin.conf ~/.kube/config
|
||||
|
||||
kubectl taint nodes --all node-role.kubernetes.io/master- # to allow allocating pods to the master node
|
||||
|
||||
# setup flannel network manager
|
||||
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
|
||||
|
||||
# setup nginx ingress controller
|
||||
# TODO
|
||||
|
||||
kubectl cluster-info
|
||||
kubectl get nodes
|
||||
kubectl get pods -A
|
||||
kubectl get cm -n kube-system kubeadm-config -o yaml
|
||||
```
|
||||
|
||||
[Kubernetes - ArchWiki](https://wiki.archlinux.org/index.php/Kubernetes)
|
||||
|
||||
[Kubernetes Ingress Controller with NGINX Reverse Proxy and Wildcard SSL from Let's Encrypt - Shogan.tech](https://www.shogan.co.uk/kubernetes/kubernetes-ingress-controller-with-nginx-reverse-proxy-and-wildcard-ssl-from-lets-encrypt/)
|
||||
|
||||
## certs
|
||||
|
||||
```bash
|
||||
pacman -S certbot certbot-dns-cloudflare
|
||||
echo "dns_cloudflare_api_token = <token>" > ~/.secrets/certbot/cloudflare.ini
|
||||
chmod 600 ~/.secrets/certbot/cloudflare.ini
|
||||
certbot certonly \
|
||||
--email y@uechi.io \
|
||||
--agree-tos \
|
||||
--dns-cloudflare \
|
||||
--dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \
|
||||
-d "*.uechi.io"
|
||||
openssl x509 -in /etc/letsencrypt/live/uechi.io/fullchain.pem -text
|
||||
certbot certificates
|
||||
|
||||
cat <<EOD > /etc/systemd/system/certbot.service
|
||||
[Unit]
|
||||
Description=Let's Encrypt renewal
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/certbot renew --quiet --agree-tos --deploy-hook "docker exec nginx-proxy-le /app/signal_le_service"
|
||||
EOD
|
||||
|
||||
cat <<EOD > /etc/systemd/system/certbot.timer
|
||||
[Unit]
|
||||
Description=Twice daily renewal of Let's Encrypt's certificates
|
||||
|
||||
[Timer]
|
||||
OnCalendar=0/12:00:00
|
||||
RandomizedDelaySec=1h
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
EOD
|
||||
```
|
||||
|
||||
- [Certbot - ArchWiki](https://wiki.archlinux.org/index.php/Certbot)
|
||||
- [Welcome to certbot-dns-cloudflare’s documentation! — certbot-dns-cloudflare 0 documentation](https://certbot-dns-cloudflare.readthedocs.io/en/stable/)
|
||||
- [docker-letsencrypt-nginx-proxy-companion/Standalone-certificates.md at master · nginx-proxy/docker-letsencrypt-nginx-proxy-companion](https://github.com/nginx-proxy/docker-letsencrypt-nginx-proxy-companion/blob/master/docs/Standalone-certificates.md)
|
||||
|
||||
## audio
|
||||
|
||||
```bash
|
||||
pacman -S alsa-utils # maybe requires reboot
|
||||
arecord -L # list devices
|
||||
|
||||
cat <<EOD > /etc/asound.conf
|
||||
pcm.m96k {
|
||||
type hw
|
||||
card M96k
|
||||
rate 44100
|
||||
format S32_LE
|
||||
}
|
||||
|
||||
pcm.!default {
|
||||
type plug
|
||||
slave.pcm "m96k"
|
||||
}
|
||||
EOD
|
||||
|
||||
arecord -vv /dev/null # test mic
|
||||
```
|
||||
|
||||
```
|
||||
alsamixer # gui mixer
|
||||
```
|
||||
|
||||
- [SoundcardTesting - AlsaProject](https://www.alsa-project.org/main/index.php/SoundcardTesting)
|
||||
- [Advanced Linux Sound Architecture/Troubleshooting - ArchWiki](https://wiki.archlinux.org/index.php/Advanced_Linux_Sound_Architecture/Troubleshooting#Microphone)
|
||||
- [ALSA project - the C library reference: PCM (digital audio) plugins](https://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html)
|
||||
|
||||
# Maintenance
|
||||
|
||||
```bash
|
||||
systemctl --failed
|
||||
free -h
|
||||
htop
|
||||
lsblk -f
|
||||
nvidia-smi
|
||||
iotop
|
||||
sensors
|
||||
journalctl -p err
|
||||
networkctl status
|
||||
```
|
18
source/_posts/2021/braille.md
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
title: 点字の表現力
|
||||
date: 2021-02-13
|
||||
---
|
||||
|
||||
「n 種類の文字を表現できる点字を作らないといけなくなったらどうしよう」
|
||||
|
||||
例としてドット が 3 つの点字を用意した。
|
||||
|
||||
- 組み合わせ数は 3P3 + 3P2 + 3P1
|
||||
- $f(N,K) = \sum_{i=K → 0} N P K$
|
||||
- 2 の n 乗っぽい
|
||||
- べき集合の濃度?
|
||||
- 2 進数のブール配列と考えるとわかりやすくなった
|
||||
- ということは X 個の表現をするために必要なブール配列の長さは $\lceil\log_2 (X)\rceil$
|
||||
- 例えばアルファベットなら 6 ドットの点字で表現できる
|
||||
- だから英語の点字は 6 つの点
|
||||
- 点字を 18 ドットに拡張すれば Unicode 13.0 の文字すべてを表現できる
|
102
source/_posts/2021/oauth-jwt-rfcs.md
Normal file
@ -0,0 +1,102 @@
|
||||
---
|
||||
title: OAuth 2.0 と JWT 関連 RFC
|
||||
date: 2021-02-11
|
||||
---
|
||||
|
||||
個人的な調査のために OAuth 2.0 と JWT 関連 RFC を発行日順に並べています。
|
||||
|
||||
## [RFC6749](https://tools.ietf.org/html/rfc6749) — The OAuth 2.0 Authorization Framework
|
||||
|
||||
2012 年 10 月
|
||||
|
||||
OAuth 1.0a に代わる新たな認証基盤 OAuth 2.0 のコアを規定しており、特筆すべき点がいくつかある。
|
||||
|
||||
- `access_token` の内容は規定されておらず、ベンダーに委ねられている
|
||||
- JWS でもなんでもいい
|
||||
- リソースサーバーに `access_token` を渡す方法は規定されていない(同月発行の RFC6750 で規定された)
|
||||
|
||||
### Authorization Grant
|
||||
|
||||
トークンエンドポイントで`access_token`を発行してもらう際に使用できる Grant (許可証)は、提案中の拡張仕様を含めて 5 つある。
|
||||
|
||||
1. Authorization Code Grant: [RFC6749 – Section 1.3.1](https://tools.ietf.org/html/rfc6749#section-1.3.1)
|
||||
1. `grant_type=authorization_code`
|
||||
2. Authorization Code Grant with PKCE
|
||||
2. Implicit Flow: [RFC6749 – Section 1.3.2](https://tools.ietf.org/html/rfc6749#section-1.3.2)
|
||||
1. もともと CORS (Cross Origin Resource Sharing) が登場する以前の SPA で、POST リクエストを回避しつつ Access Token を得る"妥協案"として策定された
|
||||
2. CSRF 耐性が無い ([RFC6819 - Section 4.4.2.5](https://tools.ietf.org/html/rfc6819#section-4.4.2.5))ため、使うべきではない
|
||||
3. Resource Owner Password Credentials Grant: [RFC6749 – Section 1.3.3](https://tools.ietf.org/html/rfc6749#section-1.3.3)
|
||||
1. 直接パスワードで認証する形式
|
||||
4. Client Credentials Grant: [RFC6749 – Section 1.3.4](https://tools.ietf.org/html/rfc6749#section-1.3.4)
|
||||
1. クライアントシークレットでトークンを取得する形式。
|
||||
5. Device Grant: [RFC Draft — OAuth 2.0 Device Authorization Grant](https://tools.ietf.org/html/draft-ietf-oauth-device-flow-15)
|
||||
1. 入力機器が無い場合もある組み込みデバイス向けの認証フロー
|
||||
|
||||
## [RFC6750](https://tools.ietf.org/html/rfc6750) — The OAuth 2.0 Authorization Framework: Bearer Token Usage
|
||||
|
||||
2012 年 10 月
|
||||
|
||||
OAuth 2.0 において、`access_token`をリソースサーバーに渡す手法を規定する。OAuth 2.0 JWT Bearer Token Flow**ではない**。
|
||||
|
||||
手法として 3 つが挙げられている。
|
||||
|
||||
1. Bearer Token (**SHOULD**)
|
||||
2. Form Encoded Parameters (SHOULD NOT)
|
||||
3. URI Query Parameters (SHOULD NOT)
|
||||
|
||||
## [OICD](https://openid.net/specs/openid-connect-core-1_0.html) — OpenID Connect Core 1.0
|
||||
|
||||
2014 年 11 月
|
||||
|
||||
OAuth 2.0 の上にいくつか仕様を足したサブセット。
|
||||
|
||||
## [RFC7515](https://tools.ietf.org/html/rfc7515) — JSON Web Signature (JWS)
|
||||
|
||||
2015 年 5 月
|
||||
|
||||
JSON ベースの署名プロトコル。
|
||||
|
||||
## [RFC7516](https://tools.ietf.org/html/rfc7516) — JSON Web Encryption (JWE)
|
||||
|
||||
2015 年 5 月
|
||||
|
||||
JSON ベースの暗号化プロトコル。
|
||||
|
||||
## [RFC7517](https://tools.ietf.org/html/rfc7517) — JSON Web Key (JWK)
|
||||
|
||||
2015 年 5 月
|
||||
|
||||
JWT の署名チェックに用いる公開鍵を配信するためのプロトコル。
|
||||
|
||||
## [RFC7518](https://tools.ietf.org/html/rfc7518) — JSON Web Algorithms (JWA)
|
||||
|
||||
2015 年 5 月
|
||||
|
||||
JWS、JWE、JWK で利用されるアルゴリズム (alg)やその他プロパティを規定する。
|
||||
|
||||
## [RFC7519](https://tools.ietf.org/html/rfc7519) — JSON Web Token (JWT)
|
||||
|
||||
2015 年 5 月
|
||||
|
||||
JWT は JSON を利用して Assertion を生成するための仕様。
|
||||
|
||||
## [RFC7521](https://tools.ietf.org/html/rfc7521) — Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants
|
||||
|
||||
2015 年 5 月
|
||||
|
||||
任意の Assertion を OAuth 2.0 Client Authentication の Client Credentials として使ったり、あるいは Authorization Grant として Access Token と交換するための仕様。
|
||||
|
||||
トークンエンドポイントに強化されたクライアント認証を付与する。続く RFC で、それぞれ SAML と JWT を使用したパターンを規定している。
|
||||
|
||||
**OAuth 2.0 JWT Bearer Token Flow**とも呼ばれている。
|
||||
|
||||
- [RFC7522](https://tools.ietf.org/html/rfc7522) — Security Assertion Markup Language (**SAML**) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants (2015 年 5 月)
|
||||
- [RFC7523](https://tools.ietf.org/html/rfc7523) — JSON Web Token (**JWT**) Profile for OAuth 2.0 Client Authentication and Authorization Grants (2015 年 5 月)
|
||||
|
||||
2015 年 5 月 https://tools.ietf.org/html/rfc7523
|
||||
|
||||
## [RFC Draft](https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-02) — JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens
|
||||
|
||||
2019 年 7 月
|
||||
|
||||
リソースサーバーに渡す Access Token に JWT を使用することを定めている。
|
148
source/_posts/2021/split-bill.md
Normal file
@ -0,0 +1,148 @@
|
||||
---
|
||||
title: 最小送金回数で精算する割り勘アルゴリズム
|
||||
date: 2021-02-14
|
||||
---
|
||||
|
||||
大人数でキャンプを楽しんだあとに待っているのは耐え難き送金処理です。
|
||||
次回から楽をするために、送金回数を最小化する制約で精算表を作る方法を考えてみます。
|
||||
|
||||
# tl;dr
|
||||
|
||||
アイディアは「最も支払わなかった人が最も支払った人に払えるだけ払う ⇢ 債権を再計算して繰り返す」です。
|
||||
|
||||
1. 全員の出費を算出(払い過ぎは正、払わなさすぎは負の数)
|
||||
2. 降順でソート(出費過多が先頭)
|
||||
3. リストの最後(最大債務者, 出費=L)がリストの最初(最大債権者, F)に $\min(F, |L|)$ を支払ってバランスを再計算
|
||||
4. 全員のバランスが 0 になるまで 2-3 を繰り返す
|
||||
|
||||
# 実験
|
||||
|
||||
実際にコードを書いて本当に望んでいる結果が得られるのかを検証します。
|
||||
|
||||
```5js
|
||||
const history = [
|
||||
{
|
||||
amount: 121,
|
||||
payer: "A",
|
||||
involves: ["A", "B", "C"],
|
||||
},
|
||||
{
|
||||
amount: 98,
|
||||
payer: "B",
|
||||
involves: ["A", "B", "C"],
|
||||
},
|
||||
{
|
||||
amount: 10,
|
||||
payer: "C",
|
||||
involves: ["A", "B", "C"],
|
||||
},
|
||||
{
|
||||
amount: 10,
|
||||
payer: "C",
|
||||
involves: ["A", "B"],
|
||||
},
|
||||
{
|
||||
amount: 50,
|
||||
payer: "C",
|
||||
involves: ["A"], // meaning C lent A 50
|
||||
},
|
||||
];
|
||||
|
||||
// calculate balance sheet
|
||||
const init = { balance: 0, consumption: 0 };
|
||||
Map.prototype.fetch = function (id) {
|
||||
return (
|
||||
this.get(id) || this.set(id, Object.assign({ name: id }, init)).get(id)
|
||||
);
|
||||
};
|
||||
|
||||
const data = new Map();
|
||||
|
||||
for (const { payer, amount, involves } of history) {
|
||||
const record = data.fetch(payer);
|
||||
record.balance += amount;
|
||||
const dept = Math.ceil(amount / involves.length);
|
||||
// actual payer should not owe extra dept coming from rounded up numbers
|
||||
const payerDept = amount - dept * (involves.length - 1);
|
||||
for (const deptor of involves.map((i) => data.fetch(i))) {
|
||||
const cost = Math.round(amount / involves.length);
|
||||
deptor.balance -= cost;
|
||||
deptor.consumption += cost;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
|
||||
// calculate transaction table
|
||||
const transaction = [];
|
||||
let paidTooMuch, paidLess;
|
||||
while (true) {
|
||||
for (const [_, tbl] of data) {
|
||||
if (tbl.balance >= (paidTooMuch?.balance || 0)) {
|
||||
paidTooMuch = tbl;
|
||||
}
|
||||
if (tbl.balance <= (paidLess?.balance || 0)) {
|
||||
paidLess = tbl;
|
||||
}
|
||||
}
|
||||
|
||||
if (paidLess.balance == 0 || paidTooMuch.balance == 0) break;
|
||||
|
||||
const amount = Math.min(paidTooMuch.balance, Math.abs(paidLess.balance));
|
||||
|
||||
transaction.push({
|
||||
sender: paidLess.name,
|
||||
receiver: paidTooMuch.name,
|
||||
amount,
|
||||
});
|
||||
|
||||
paidTooMuch.balance -= amount;
|
||||
paidLess.balance += amount;
|
||||
}
|
||||
|
||||
console.log("Settled");
|
||||
|
||||
console.log("\n# Transaction table");
|
||||
for (const ev of transaction) {
|
||||
console.log(`${ev.sender} owes ${ev.receiver} ¥${ev.amount}`);
|
||||
}
|
||||
|
||||
console.log("\n# History");
|
||||
for (const { payer, amount, involves } of history) {
|
||||
if (involves.length === 1) {
|
||||
console.log(`${payer} lent ¥${amount} to ${involves[0]}`);
|
||||
} else {
|
||||
console.log(`${payer} paid ¥${amount} for ${involves.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("\n# Expenses");
|
||||
for (const [_, { name, consumption }] of data) {
|
||||
console.log(`${name} virtually paid ¥${consumption} in total`);
|
||||
}
|
||||
```
|
||||
|
||||
`history`に支払い履歴を書き込んでから実行すると、「送金表」「履歴」「実質支払総額」が得られます。
|
||||
|
||||
```md
|
||||
# Transaction table
|
||||
|
||||
A owes B ¥10
|
||||
C owes B ¥6
|
||||
|
||||
# History
|
||||
|
||||
A paid ¥121 for A, B, C
|
||||
B paid ¥98 for A, B, C
|
||||
C paid ¥10 for A, B, C
|
||||
C paid ¥10 for A, B
|
||||
C lent ¥50 to A
|
||||
|
||||
# Expenses
|
||||
|
||||
A virtually paid ¥131 in total
|
||||
B virtually paid ¥81 in total
|
||||
C virtually paid ¥76 in total
|
||||
```
|
||||
|
||||
プログラムに落とし込めたら、あとはアプリを作るなりスプレッドシートのマクロにするなり自由です。面倒なことは全部コンピューターにやらせよう!
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |