A place to cache linked articles (think custom and personal wayback machine)
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

3 年前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="fr">
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset="utf-8">
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name="viewport" content="width=device-width,initial-scale=1">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>Publishing a Website the Modern Way (archive) — David Larlet</title>
  13. <meta name="description" content="Publication mise en cache pour en conserver une trace.">
  14. <!-- That good ol' feed, subscribe :). -->
  15. <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
  16. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  17. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
  18. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
  19. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
  20. <link rel="manifest" href="/static/david/icons2/site.webmanifest">
  21. <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
  22. <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
  23. <meta name="msapplication-TileColor" content="#f0f0ea">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f0f0ea">
  26. <!-- Documented, feel free to shoot an email. -->
  27. <link rel="stylesheet" href="/static/david/css/style_2020-06-19.css">
  28. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  29. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  30. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  31. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  32. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <script type="text/javascript">
  36. function toggleTheme(themeName) {
  37. document.documentElement.classList.toggle(
  38. 'forced-dark',
  39. themeName === 'dark'
  40. )
  41. document.documentElement.classList.toggle(
  42. 'forced-light',
  43. themeName === 'light'
  44. )
  45. }
  46. const selectedTheme = localStorage.getItem('theme')
  47. if (selectedTheme !== 'undefined') {
  48. toggleTheme(selectedTheme)
  49. }
  50. </script>
  51. <meta name="robots" content="noindex, nofollow">
  52. <meta content="origin-when-cross-origin" name="referrer">
  53. <!-- Canonical URL for SEO purposes -->
  54. <link rel="canonical" href="https://www.jpatters.com/the-modern-way/">
  55. <body class="remarkdown h1-underline h2-underline h3-underline hr-center ul-star pre-tick">
  56. <article>
  57. <header>
  58. <h1>Publishing a Website the Modern Way</h1>
  59. </header>
  60. <nav>
  61. <p class="center">
  62. <a href="/david/" title="Aller à l’accueil">🏠</a> •
  63. <a href="https://www.jpatters.com/the-modern-way/" title="Lien vers le contenu original">Source originale</a>
  64. </p>
  65. </nav>
  66. <hr>
  67. <main>
  68. <p>I am a proficient and successful software developer. I have been putting websites on the internet for over 15 years. Yet I have never been as frustrated in doing so as I have been today.</p>
  69. <p>It used to be that you would sign up for a shared hosting account for like $3/month and click the “WordPress” button in your cPanel. Then, after updating your DNS records to point to the IP address of the shared hosting server, you could visit yourdomain.com and see the WordPress setup page. Another ten minutes of fiddling with phpMyAdmin and you would have a WordPress website. In 15 minutes or less you would have launched a new website on “your own” server. Could it stand up to the front page of hacker news? Nope. But it didn’t really matter, this is how it was done.</p>
  70. <p><img src="https://www.jpatters.com/images/wordpress-setup.webp" alt="the wordpress setup page"/></p>
  71. <p>In trying to start this blog I decided I would use modern technologies. Afterall, I do have a <a href="https://tina.io" target="_blank" rel="noopener nofollow noreferrer">startup in the jamstack space</a>. I’ve been a huge fan of <a href="https://gohugo.io" target="_blank" rel="noopener nofollow noreferrer">Hugo</a> since I found it when it was at version 0.11 in May of 2014. And I love golang, so this was an easy choice. I wanted to “own the whole stack” so I opted to not use <a href="https://netlify.com" target="_blank" rel="noopener nofollow noreferrer">Netlify</a> or <a href="https://vercel.com" target="_blank" rel="noopener nofollow noreferrer">Vercel</a> for hosting. Instead, since I am well versed in AWS services having been a user since 2011, I decided to put the site on S3 and serve it with CloudFront. This should be easy... right?</p>
  72. <p>Wow, was I wrong.</p>
  73. <h4 id="attempt-number-1">Attempt Number 1</h4>
  74. <p>I’ll create a hosted zone in Route 53 and set the nameservers on my domain in my registrar. Next I’ll create an S3 bucket to hold the contents of my website. Then over to cloudfront to create a distribution and point it at the bucket. And finally, create a new DNS record to point my domain at CloudFront. Done.</p>
  75. <p><em>Unable to connect to origin.</em></p>
  76. <p>Hmmmm.</p>
  77. <p>Oh, right, the bucket needs “Static Web Hosting” turned on. I totally forgot to do that.</p>
  78. <p><em>Unable to connect to origin.</em></p>
  79. <p>Ok…</p>
  80. <p>Well, since I didn’t make the objects in the bucket publicly accessible (because I only want the website available via CloudFront) maybe I need a bucket policy. Google google google. Yep, that must be the problem. Alright, enter the policy and click save.</p>
  81. <p><em>Unable to save bucket policy. One of the principals is invalid.</em></p>
  82. <p>Double check. Triple check. That’s the correct value for the principal. Google google google. I need to tell CloudFront to create an origin access identity. How do I do that? I need to use the bucket name as the origin instead of the S3 website endpoint. Well if I do that then the website won’t be served from S3 properly. But that’s the only way to do it. I give up. There must be an easier way. Google google google.</p>
  83. <h4 id="attempt-number-2">Attempt Number 2</h4>
  84. <p>I found <a href="https://github.com/aws-samples/amazon-cloudfront-secure-static-site" target="_blank" rel="noopener nofollow noreferrer">this project from AWS</a>. This should make it simple. It’s got a “Launch on AWS” button. Perfect. Click. Enter in the values for the CloudFormation template. Wait for the stack to launch.</p>
  85. <p><em>CREATE_FAILED</em></p>
  86. <p>What now? Oh, there are still some resources from my first attempt hanging around and they are conflicting with the resources that the stack is trying to create. No problem, I’ll just delete those and relaunch the stack.</p>
  87. <p><em>CREATE_COMPLETE</em></p>
  88. <p>Awesome. And the example site is showing up on www.mydomain.com. Now I just need to deploy my site to the S3 bucket. The code for my site is on GitHub, might as well use GitHub Actions. Type type type, git push.</p>
  89. <p><em>Deploy successful.</em></p>
  90. <p>Excellent. I’m really making progress now. And my website is now showing up at www.mydomain.com. Now… how do I redirect mydomain.com to www.mydomain.com? There must be an easy way to do that in Route 53, right? Nope.</p>
  91. <p>Ok. I need to create another S3 bucket, another ACM Certificate, another CloudFront distribution, and tell the S3 bucket to redirect requests to www.mydomain.com. Seems like a lot of work for something so simple, but if that’s how I’ve got to do it then that’s how I’ve got to do it. Type type type, click click click.</p>
  92. <p>Redirect works. Great!</p>
  93. <p>This is looking awesome. Let’s check the console in Firefox and see if things are getting cached.</p>
  94. <p><em>Content Security Policy: The page’s settings blocked the loading of a resource at inline (“script-src”)</em></p>
  95. <p>Hmmm. Why would that be? The readme for the GitHub repo says that it helps create a “Secure Static Website” and uses Lambda@Edge. Wait a minute, I thought this was a static website. But I’m running lambda functions now? The readme also has a section on updating the content security policy.</p>
  96. <ol>
  97. <li>
  98. <p>Make your changes to the header values by editing <code>source/secured-headers/index.js</code>.</p>
  99. </li>
  100. <li>
  101. <p>Deploy the solution by following the steps in <a href="https://github.com/aws-samples/amazon-cloudfront-secure-static-site#update-the-website-content-locally" target="_blank" rel="noopener nofollow noreferrer">Update the website content locally</a></p>
  102. </li>
  103. </ol>
  104. <p>“Update the website content locally” says to install npm, clone the repo, build the artifacts, copy my website into a particular folder, and run the following S3 commands.</p>
  105. <p>What? I just want to get my blog online. When did this become rocket science? I am done for the night. Going to bed. I will try again tomorrow.</p>
  106. </main>
  107. </article>
  108. <hr>
  109. <footer>
  110. <p>
  111. <a href="/david/" title="Aller à l’accueil">🏠</a> •
  112. <a href="/david/log/" title="Accès au flux RSS">🤖</a> •
  113. <a href="http://larlet.com" title="Go to my English profile" data-instant>🇨🇦</a> •
  114. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">📮</a> •
  115. <abbr title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340">🧚</abbr>
  116. </p>
  117. <template id="theme-selector">
  118. <form>
  119. <fieldset>
  120. <legend>Thème</legend>
  121. <label>
  122. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  123. </label>
  124. <label>
  125. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  126. </label>
  127. <label>
  128. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  129. </label>
  130. </fieldset>
  131. </form>
  132. </template>
  133. </footer>
  134. <script type="text/javascript">
  135. function loadThemeForm(templateName) {
  136. const themeSelectorTemplate = document.querySelector(templateName)
  137. const form = themeSelectorTemplate.content.firstElementChild
  138. themeSelectorTemplate.replaceWith(form)
  139. form.addEventListener('change', (e) => {
  140. const chosenColorScheme = e.target.value
  141. localStorage.setItem('theme', chosenColorScheme)
  142. toggleTheme(chosenColorScheme)
  143. })
  144. const selectedTheme = localStorage.getItem('theme')
  145. if (selectedTheme && selectedTheme !== 'undefined') {
  146. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  147. }
  148. }
  149. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  150. window.addEventListener('load', () => {
  151. let hasDarkRules = false
  152. for (const styleSheet of Array.from(document.styleSheets)) {
  153. let mediaRules = []
  154. for (const cssRule of styleSheet.cssRules) {
  155. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  156. continue
  157. }
  158. // WARNING: Safari does not have/supports `conditionText`.
  159. if (cssRule.conditionText) {
  160. if (cssRule.conditionText !== prefersColorSchemeDark) {
  161. continue
  162. }
  163. } else {
  164. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  165. continue
  166. }
  167. }
  168. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  169. }
  170. // WARNING: do not try to insert a Rule to a styleSheet you are
  171. // currently iterating on, otherwise the browser will be stuck
  172. // in a infinite loop…
  173. for (const mediaRule of mediaRules) {
  174. styleSheet.insertRule(mediaRule.cssText)
  175. hasDarkRules = true
  176. }
  177. }
  178. if (hasDarkRules) {
  179. loadThemeForm('#theme-selector')
  180. }
  181. })
  182. </script>
  183. </body>
  184. </html>