Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 2675

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 61k
Author
Asked: November 26, 20242024-11-26T07:36:08+00:00 2024-11-26T07:36:08+00:00

Migration of a Dynamic Website to a Static Website

  • 61k

Photo by Sai Abhinivesh Burla on Unsplash

In the previous two articles (Migration from Classic Hosting to Serverless and Migration of a Multiplayer Game from Hosted to Serverless) I've introduced you to my plan of migrating away from my dedicated server to a fully serverless infrastructure. This time I want to go into the plan in more detail.

Why Migrate?!

In general, I expect from this migration:

  • A more clean code base (finally I can clean up my stuff, maybe remove something and modernize some other parts)
  • No more FTP or messy / unclear deployments – everything should be handled by CI/CD pipelines
  • Cost reduction; sounds weird, but the last thing I want to have is a cost increase (today it's about 30 € per month for the hosting and my goal is to bring this below or close to 10 € – note: I pay much more for domains and these costs are not included here as they will remain the same).

There is a bit of background to this: Having my own dedicated server is something I was initially was happy about, however, over all the years the burden of properly maintaining this machine was a bit too high. I have quite some things on my plate and dealing with the (software-side) of a dedicated server was always on the bottom part of my ToDo list.

Over all the years the idea to move all parts of my server to the cloud certainly started to appeal. However, one thing that always stood in the way was email. I want (and need) my email to be also hosted, and right now the only option would be to set up a virtual machine (VM) at some provider… which is what I want to avoid badly! Not only cost VMs at the popular cloud providers more than my current dedicated hosting, that also would require me to be a mail admin. And trust me – the last thing I want to be is a mail admin. It's hard.

Now I needed to look for a solution as – for the second time since I have a dedicated server – the configuration I use is phased out at my hosting provider. Like the first time there is no comparable configuration available, so I need to get something more beefy (and more expensive). This time, however, I'll skip this mandatory upgrade; I move to the cloud.

This is already final – as shown below (German “Kündigung” means “cancellation” or “termination” of the service):

Quitting the dedicated server

Still, I first need to solve the problem of email.

Solving Email

I did a bit of research on the available mail providers:

  • Google, Microsoft etc.
  • Proton Mail
  • Zoho Mail
  • Posteo
  • Yandex
  • Fastmail

In the end I went with Fastmail. Why? Quite simple – it pretty much solves the core problem of my Cloud migration in a way that makes the whole migration work without much trouble. How?!

Fastmail offers the possibility of using custom domains. This way you don't get just a user@fastmail.com address, but rather a chief@yourdomain.com. While most other mail providers also offer custom domain support Fastmail has for a budget of $5 a custom DNS on top of it – supporting up to 100 custom domains (enough for me) with free configuration. That's right – it's essentially a $5 DNS service with email.

But Fastmail does not stop there. One other problem would be that I register a custom domain such as florian-rappl.de but then I could only set a CNAME for www.florian-rappl.de leaving florian-rappl.de undefined. While most browsers will handle this scenario very nicely (with an auto-completion of the www subdomain) there is one browser that makes trouble here; but you'd need to get on a Safari to find it. How does Fastmail solve this root domain problem? With static websites!

On Fastmail you can create a redirect for the custom domain. So I can just make https://florian-rappl.de respond automatically with a 301 (permanently moved) to https://www.florian-rappl.de. Ah and, of course, the website is automatically HTTPs protected by a Let's Encrypt certificate.

With email solved it's time to look where I've been and where I want to be from an architecture point of view.

Previous Architecture

Previously, everything was hosted on a dedicated server. The whole DNS service, mail and websites have all been served from the same machine, which also hosted the database. To bring updated content to the server FTP has been used. Any kind of configuration was done manually.

The following diagram illustrates this.

Previous architecture

There are three aspects that I want to see moving to a new architecture:

  • It should be more flexible, i.e., leaving room to use other languages and paradigms
  • It should be more efficient, i.e., being able to serve my page (or any other project) even faster
  • It should be more easy for me to grasp and maintain

All this should be fulfilled without going over the previous budget. Ideally, it should be even cheaper than beforehand. Just as a number: My page has around 40k views per month – so this is the load that I expect and that the new architecture should be able to handle this kind of load well.

Below I've sketched how I envision the new architecture. Configuration and updates in this scheme are all done via CI/CD. The mail provider also has an extensive API allowing me to send and receive emails automatically / have an AI assistant in between (more on that aspect later).

Anticipated architecture

This is, of course, quite high-level. The actual details, i.e., where each website that I have is rolled out and how these boxes connect in all detail are to be determined in a more low-level diagram.

In general I will not remove any website. I am a huge proponent of reliable URLs, i.e., I will not drop or change URLs intentionally. Anything that was on the web last year should still be on the web. One thing, however, is that not everything needs to remain as-is, but I am free to change (or simplify) if it keeps the previous content (mostly) intact.

One example is a website I did for a lecture I gave on software design patterns. For this I decided to make a dynamic to static conversion.

Dynamic to Static Conversion

For patterns.florian-rappl.de I wanted to remove the dynamic part. This was a bit difficult, as the whole page (every presentation and slide) has been generated dynamically by a custom CMS.

Instead, what I ended up doing is utilizing AngleSharp for transforming the existing (dynamic) websites into static files. I've stored them on disk and made them ready to be served statically.

The following script was used to get the initial download of the static pages:

List<string> downloadedUrls = new ();  async Task Main() {     var url = "https://patterns.florian-rappl.de/";     var target = "~/code/florian-rappl-patterns/public";     await DownloadPage(url, target); }  void CreateIfNotExists(string dir) {     if (!Directory.Exists(dir))     {         Directory.CreateDirectory(dir);     } }  async Task DownloadAsset(Url url, string targetDir) {     if (!downloadedUrls.Contains(url.Href))     {         var file = Path.Combine(targetDir, url.Path);         var dir = Path.GetDirectoryName(file);         CreateIfNotExists(dir);          var client = new HttpClient();         using var stream = await client.GetStreamAsync(url.Href);         using var fs = File.Create(file);         await stream.CopyToAsync(fs);     } }  async Task DownloadPage(Url url, string targetDir) {     // Don't download these - they wouldn't be useful and will be removed     if (url.Path.StartsWith("Account") || url.Path.StartsWith("Slides"))     {         return;     }      // only download websites within the origin     if (!downloadedUrls.Contains(url.Href))     {         var dir = Path.Combine(targetDir, url.Path);         var indexPath = Path.Combine(dir, "index.html");         var config = Configuration.Default.WithRequesters().WithDefaultLoader();         var context = BrowsingContext.New(config);         var document = await context.OpenAsync(url);          CreateIfNotExists(dir);         File.WriteAllText(indexPath, document.Source.Text);          downloadedUrls.Add(url.Href);          // download all stylesheets         foreach (var link in document.QuerySelectorAll<IHtmlLinkElement>("link[href]"))         {             var href = link.Href;             await DownloadAsset(href, targetDir);         }          // download all scripts         foreach (var link in document.QuerySelectorAll<IHtmlScriptElement>("script[src]"))         {             var href = link.Source;             await DownloadAsset(new Url(href, url.Href), targetDir);         }          // download all images         foreach (var link in document.QuerySelectorAll<IHtmlImageElement>("img[src]"))         {             var href = link.Source;             await DownloadAsset(href, targetDir);         }          // follow all links         foreach (var anchor in document.QuerySelectorAll<IHtmlAnchorElement>("a"))         {             var href = anchor.Href;              if (href.StartsWith(url.Origin))             {                 await DownloadPage(href, targetDir);             }         }     } } 
Enter fullscreen mode Exit fullscreen mode

After the script was applied we have all the available pages downloaded and available for being served in a static website.

Using a search and replace I've also added a few enhancements such as using a file like jquery.js instead of the previously given URL /bundles/jquery?someid, which was originally leading to a bundle endpoint that performed some MVC magic.

Another thing I did via a search and replace was to transform the URLs for the UML diagrams (usually something like /diagrams/1d830940-8feb-4c70-b355-b5370cfcd825) to a proper SVG reference (/diagrams/1d830940-8feb-4c70-b355-b5370cfcd825.svg). With a bit more time invest I could have also renamed that properly, e.g., /diagrams/mvc-pattern.svg, but having the proper extension is good enough for now.

The result of the static-ification of the website is seen below. Full URLs transformed into a folder structure with an index.html. Surely, a bit of a nicer transformation would use something like Astro with proper re-use, however, the effort for such a transformation would have been quite higher. If the website would still be actively used or I'd envision some progression here in the following years I potentially would invest the time, but right now it does not seem to be needed.

Structure after static-ification

The deployment is done via an Azure Pipeline:

trigger: - master  pool:   vmImage: ubuntu-latest  variables: - group: deployment-tokens  steps: - task: AzureStaticWebApp@0   inputs:     app_location: '/public'     api_location: '/api'     skip_app_build: true     azure_static_web_apps_api_token: '$(patterns-token)' 
Enter fullscreen mode Exit fullscreen mode

The deployment is done to Azure Static Web App. This is perfect for the scenario at hand; a mostly static website with a few APIs (in this case exclusively used for the search). Note that the free tier of Azure SWA is used, i.e., this is another area of my website that is now running at essentially zero cost.

Conclusion

It runs – faster and more cost efficient (for the given subdomain no additional costs will occur). The crucial part was to identify a way of providing the content in a mode that fits its purpose best.

Making the patterns website static was the right choice – the dynamic nature of this page was not longer required. All the material has been created and the individual HTML files are sufficient for keeping the previous user experience alive.

In the next post I'll look into the migration of a game (Mario 5) with its level editor and backend API.

Currently, the dedicated server is still operational – but I need to finish the migration until the end of the year.

azurecloudcsharpwebdev
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 1k
  • Popular
  • Answers
  • Author

    How to ensure that all the routes on my Symfony ...

    • 0 Answers
  • Author

    Insights into Forms in Flask

    • 0 Answers
  • Author

    Kick Start Your Next Project With Holo Theme

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.