簡而言之 你要在DNS加一筆record
每次更新他會要求你放不同的值
https://community.letsencrypt.org/t/i-am-confused-about-dns-challenge/18754
每天的 1%
筆記一些每天學習到的東西,希望一年可以至少筆記 365 篇,一天 1% 一年就可以進步 37.8 倍
2019年3月12日 星期二
2018年12月8日 星期六
Heoku pipeline stateless builds
Heroku pipeline 從 staging promot 到 production 的時候會把 compile 過的 file 一起 copy 過去,所以如果用到 Node + Webpack 讀取環境變數再 compile 成 js 的 file 就會吃到 staging 的環境變數而不是 production 的環境變數,這又稱為 stateful builds,可以看 https://github.com/heroku/heroku-buildpack-nodejs/issues/545 有更多的討論
解決方式可以參考 https://github.com/nholden/crawl_vote/commit/cd1b89dff4812da1270d6a5d54b594aecb1a5ca7 把一些變數放到 window裡面,這樣就會吃到正確的變數
https://devcenter.heroku.com/articles/pipelines#design-considerations
rspec test protocal (interface) - part 2 - define our own DSL for rspec
接續上一篇 https://everyday1percent.blogspot.com/2018/05/rspec-test-protocal-interface.html
現在我們要做一個客製化的 rspec matcher,達到的效果如下:
```ruby
RSpec.describe FooService do
describe "interfaces" do
specify do
expect(FooService).to has_implemented_interfaces :call, :call!
end
end
end
```
可以先參考 rspec 的 custom matcher 應該就可以做到了
https://relishapp.com/rspec/rspec-expectations/docs/custom-matchers/define-a-matcher-supporting-block-expectations
如果要做成 gem 的話可以參考 rspec-expectations 開放的 protocal
https://github.com/rspec/rspec-expectations/blob/master/lib/rspec/matchers/matcher_protocol.rb
https://makandracards.com/makandra/662-write-custom-rspec-matchers
https://github.com/kucaahbe/rspec-html-matchers
在 Rails 使用 custom font
參考 https://coderwall.com/p/v5c8kq/web-fonts-and-rails-asset-pipeline 把檔案放到 `app/assets/fonts` 裡面
然後在 `application.scss` 裡加上
```scss
@font-face {
font-family: 'Calibre';
src: font-url('calibre/regular/Calibre-Regular.otf') format('opentype'),
font-url('calibre/regular/Calibre-Regular.woff') format('woff'),
font-url('calibre/regular/Calibre-Regular.ttf') format('truetype'),
font-url('calibre/regular/Calibre-Regular.svg#Calibre-Regular') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Calibre';
src: font-url('calibre/medium/Calibre-Semibold.woff2') format('woff');\
font-weight: bold;
font-style: bold;
}
b, strong {
font-weight:bold
}
h1, h2, h3, h4, h5, h6 {
font-weight:bold
}
這樣就行
(font-face 可以放在別的 file 再 import 也可以)
https://coderwall.com/p/v5c8kq/web-fonts-and-rails-asset-pipeline
https://github.com/nicotaing/chloepark/tree/master/themes/park/source/fonts/calibre/regular
2018年12月7日 星期五
Ruby instance_eval & block
```rb
class Test
def initialize(&block)
@name = "Hello World"
instance_eval(&block) if block_given?
end
def name
@name
end
end
```
這樣是完全可以的,因為在 block 裡面的 `self` 變成了 `Test` instance
```rb
Test.new do
puts name
end
# => Hello World
```
2018年12月2日 星期日
Ansible + rails + letsencrypt
First, install nginx
roles/nginx/tasks/main.yml
```yml
- name: Install nginx
apt:
name: nginx
state: latest
- name: Disable default nginx config
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify:
- Restart nginx
```
roles/nginx/handlers/main.yml
```yml
- name: Restart nginx
service: name=nginx state=restarted
```
Then, install LetsEncrypt
roles/letsencrypt/tasks/main.yml
```yml
- name: Generate dhparams file
shell: openssl dhparam -out {{ dhparam_pem_path }} 2048
args:
creates: "{{ dhparam_pem_path }}"
- name: Update apt cache
apt: update_cache=yes cache_valid_time=86400
- name: Install depends
apt: name={{ item }} state=latest
with_items:
- python
- python-dev
- gcc
- dialog
- libaugeas0
- augeas-lenses
- libssl-dev
- libffi-dev
- ca-certificates
- python-pip
- python-virtualenv
- git
- libpython-dev
- zlib1g-dev
- name: Lets Encrypt client
git: dest=/opt/certbot clone=yes repo=https://github.com/certbot/certbot force=yes
# Auto-renew certificates and reload nginx
- name: Add crontab to renew certificates
cron: minute="30" hour="2" weekday="1" job="/opt/certbot/certbot-auto renew >> /var/log/le-renew.log"
- name: Add crontab to reload server
cron: minute="35" hour="2" weekday="1" job="/etc/init.d/nginx reload"
```
Last, configure service
roles/service/tasks/main.yml
```yml
- name: Add http nginx configuration
template:
src: templates/http.conf.j2
dest: /etc/nginx/sites-available/{{ service_name }}.http.conf
notify:
- Restart nginx
- name: Enable nginx config
file:
src: /etc/nginx/sites-available/{{ service_name }}.http.conf
dest: /etc/nginx/sites-enabled/{{ service_name }}.http.conf
state: link
notify:
- Restart nginx
- name: Create letsencrypt certificate
shell: ./certbot-auto certonly --webroot --email {{ service_admin_email }} --agree-tos --webroot-path=/usr/share/nginx/html -d {{ service_host }};
args:
chdir: /opt/certbot
- name: Add https nginx configuration
template:
src: templates/https.conf.j2
dest: /etc/nginx/sites-available/{{ service_name }}.https.conf
notify:
- Restart nginx
- name: Add external https nginx symlink
file:
src: /etc/nginx/sites-available/{{ service_name }}.https.conf
dest: /etc/nginx/sites-enabled/{{ service_name }}.https
state: link
notify:
- Restart nginx
```
with nginx http template, this will redirect all http request into https
roles/service/templates/http.conf.j2
```
server {
listen 80;
server_name {{ service_host }};
root /usr/share/nginx/html;
index index.html index.htm index.nginx-debian.html;
location ~ /.well-known {
allow all;
}
location / {
return 301 https://$host$request_uri;
}
}
```
also add a https template for rails
roles/service/templates/https.conf.j2
```
upstream app {
server unix:///home/deploy/apps/waynechu_blog/shared/tmp/sockets/puma.sock fail_timeout=0;
}
server {
listen 443 ssl;
server_name {{ service_host }};
root /home/deploy/apps/{{ service_name }}/current/public;
try_files $uri/index.html $uri @app;
ssl_certificate {{ letsencrypt_ssl_dir }}/{{ service_host }}/fullchain.pem;
ssl_certificate_key {{ letsencrypt_ssl_dir }}/{{ service_host }}/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_dhparam {{ dhparam_pem_path }};
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_stapling on;
ssl_stapling_verify on;
location @app {
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 10M;
keepalive_timeout 10;
}
```
roles/service/handlers/main.yml
```yml
- name: Restart nginx
service: name=nginx state=restarted
```
At the last, set your own vars
vars/main.yml
```yml
service_host: "example.cc"
service_name: "example"
service_admin_email: "example@gmail.com"
letsencrypt_ssl_dir: "/etc/letsencrypt/live"
dhparam_pem_path: "/home/deploy/dhparams.pem"
```
That's it, now you have a https rails web
References:
https://medium.com/@gmaliar/generating-lets-encrypt-certificates-for-nginx-using-ansible-9fd27b90993a
https://gist.github.com/mattiaslundberg/ba214a35060d3c8603e9b1ec8627d349
https://docs.ansible.com/ansible/2.5/modules/letsencrypt_module.html
https://gorails.com/guides/free-ssl-with-rails-and-nginx-using-let-s-encrypt
https://blog.frost.tw/posts/2018/05/28/Getting-started-deploy-your-Ruby-on-Rails-Part-8/
標籤:
Deployment,
Developer Tools,
ROR,
Server
2018年11月18日 星期日
Implement algolia autocomplete with rails
```js
// app/javascripts/application.js
const algoliasearch = require('algoliasearch/lite')
const autocomplete = require('autocomplete.js')
const client = algoliasearch(process.env.ALGOLIA_APPLICATION_ID, process.env.ALGOLIA_SEARCH_ONLY_API_KEY)
const pack = {
algoliaClient: client,
autocomplete: autocomplete
}
window.Pack = pack
```
```erb
<!-- Algolia search autocomplete HTML markup -->
<div class="aa-input-container" id="aa-input-container">
<input type="search" id="aa-search-input" class="aa-input-search" placeholder="Search multiple fields in one place..." name="search" autocomplete="off" />
<svg class="aa-input-icon" viewBox="654 -372 1664 1664">
<path d="M1806,332c0-123.3-43.8-228.8-131.5-316.5C1586.8-72.2,1481.3-116,1358-116s-228.8,43.8-316.5,131.5 C953.8,103.2,910,208.7,910,332s43.8,228.8,131.5,316.5C1129.2,736.2,1234.7,780,1358,780s228.8-43.8,316.5-131.5 C1762.2,560.8,1806,455.3,1806,332z M2318,1164c0,34.7-12.7,64.7-38,90s-55.3,38-90,38c-36,0-66-12.7-90-38l-343-342 c-119.3,82.7-252.3,124-399,124c-95.3,0-186.5-18.5-273.5-55.5s-162-87-225-150s-113-138-150-225S654,427.3,654,332 s18.5-186.5,55.5-273.5s87-162,150-225s138-113,225-150S1262.7-372,1358-372s186.5,18.5,273.5,55.5s162,87,225,150s113,138,150,225 S2062,236.7,2062,332c0,146.7-41.3,279.7-124,399l343,343C2305.7,1098.7,2318,1128.7,2318,1164z" />
</svg>
</div>
<!-- Initialize autocomplete menu -->
<script>
var index = Pack.algoliaClient.initIndex('<%= index_name %>');
Pack.autocomplete('#aa-search-input',
{ hint: false }, {
source: Pack.autocomplete.sources.hits(index, {hitsPerPage: 5, filters: 'discarded_at_i < 1'}),
displayKey: 'name',
templates: {
suggestion: function(suggestion) {
return `<a href='<%= path_prefix %>/${suggestion.objectID}'>${suggestion._highlightResult.name.value}</a>`
}
}
}).on("autocomplete:selected", function (event, suggestion, dataset) {
window.location.href = `<%= path_prefix %>/${suggestion.objectID}`;
})
</script>
```
訂閱:
文章 (Atom)