Revert "custom shiki highlighter from dominikg"
This reverts commit 9b976986d7
.
This commit is contained in:
parent
9b976986d7
commit
dc948269b9
|
@ -1,7 +1,6 @@
|
||||||
// See https://kit.svelte.dev/docs/types#app
|
// See https://kit.svelte.dev/docs/types#app
|
||||||
// for information about these interfaces
|
// for information about these interfaces
|
||||||
// and what to do when importing types
|
// and what to do when importing types
|
||||||
|
|
||||||
declare namespace App {
|
declare namespace App {
|
||||||
// interface Locals {}
|
// interface Locals {}
|
||||||
// interface PageData {}
|
// interface PageData {}
|
||||||
|
|
|
@ -24,12 +24,6 @@ body {
|
||||||
src: url('/fonts/Quicksand.ttf');
|
src: url('/fonts/Quicksand.ttf');
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose {
|
|
||||||
font-family: 'Cooper Hewitt', sans-serif;
|
|
||||||
letter-spacing: 0.25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Magilio';
|
font-family: 'Magilio';
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
|
|
||||||
tldr:
|
|
||||||
I started by containerizing my SvelteKit and Strapi apps, then Pushed these to Docker Hub and AWS ECR,
|
|
||||||
leveraging 1 free private image on docker hub and free-tier 500mb limit on AWS ECR, thereby minimizing costs and exploring each option.
|
|
||||||
|
|
||||||
1. Conteinerization
|
|
||||||
a. Sveltekit
|
|
||||||
Keeping in mind that dev, build, test and lint, etc. scripts are handled by turborepo, we use these when containerizing.
|
|
||||||
|
|
||||||
Step 1.
|
|
||||||
|
|
||||||
b. Strapi
|
|
|
@ -1,7 +0,0 @@
|
||||||
choosing a package manager
|
|
||||||
|
|
||||||
I tried to abstain from the community battles between pms and looked at the performance of the top 3 used - npm, yarn and pnpm. https://pnpm.io/benchmarks
|
|
||||||
I've got experience with each and would like to use pnpm as it is the much faster than NPM,
|
|
||||||
however Strapi doesn't officialy support it so I ended up choosing yarn as in most cases it is a bit faster than npm, has some better handling with monorepos and I personally like it.
|
|
||||||
I used Turborepo with npm and there were some problems with setting dependencies in workspaces vs the root of monorepo and private packages, which required workarounds.
|
|
||||||
In terms of CI performance can have significant consequences.
|
|
|
@ -1,15 +0,0 @@
|
||||||
AWS set up
|
|
||||||
|
|
||||||
A thing I learned using linux is user management, specifically don't do everything as root :D.
|
|
||||||
When using AWS first I didn't care about IAM roles, but I think I learned the same lesson...
|
|
||||||
|
|
||||||
Anyways I used this process, when configuring accounts:
|
|
||||||
|
|
||||||
When starting login as root user and access IAM dashboard,
|
|
||||||
Set up Multi-factor Auth as root user (prefferably with Yubikey or some OTP in a secure device)
|
|
||||||
Later on, like in linux, the root account access can be minimized for a better decentralized privilege system, even when being the only admin.
|
|
||||||
|
|
||||||
Go to services then IAM Identity center
|
|
||||||
Select identity source -> In my case stick with the default Identity Center directory, bigger organizations or ones using other sources can connect to external ones.
|
|
||||||
In Multi-account permissions select permission sets and create a permission set.
|
|
||||||
Then go to AWS accounts, select the account you wish and assign the permission set to them.
|
|
|
@ -1,62 +0,0 @@
|
||||||
Namecheap
|
|
||||||
Saw mattmor.in and just bought it, no questions asked.
|
|
||||||
|
|
||||||
I immediately created a cloudflare account, because of their great certificate management, security checks, analytics and mostly DNS.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Setting up AWS EC2 & RDS instances
|
|
||||||
|
|
||||||
EC2 setup
|
|
||||||
I've got a great opportunity to set up a "free-tier" instance, of course the limitations are quite interesting - 750 hrs/m and some compute limiting factors.
|
|
||||||
Let's use this opportunity, it is a better deal than GCP with 300$/3mos and my experience with GCP while dealing with Auth API was pretty bad.
|
|
||||||
|
|
||||||
Made SSH keys
|
|
||||||
got an Elastic IP and connected it with my EC2 instance to be accessible publicly on a stable IP.
|
|
||||||
added DNS records to cloudflare
|
|
||||||
|
|
||||||
|
|
||||||
Log in via SSH, update, upgrade everything, restart.
|
|
||||||
Then I configured fail2ban with some custom responses and enabled it, I also configured the seemingly redundant ufw and allowed ssh, https
|
|
||||||
|
|
||||||
And with that I am ready to roll onto installing necessary software like nginx, jenkins, docker for now.
|
|
||||||
|
|
||||||
RDS setup
|
|
||||||
|
|
||||||
I've already configured Strapi for my backend, I selected PostgreSQL, because while theoretically the default SQLite db would be sufficient and very easy to manage.
|
|
||||||
I know it's scalability is limited and is not suitable for multiple user access, which I might need in other projects with the same tech stack.
|
|
||||||
The main reason for choosing PostgreSQL is I can showcase it and learn with it on a project using AWS.
|
|
||||||
I've worked mainly with MariaDB before, and used PostgreSQL only on some ancient Wordpress projects back in the day.
|
|
||||||
|
|
||||||
As I already set up a free-tier EC2 instance, I can also setup AWS RDS with Aurora(PostgreSQL compatible) or PostgreSQL also on free-tier, which is awesome!
|
|
||||||
Let's choose PostgreSQL, because that's simpler and more than sufficient.
|
|
||||||
|
|
||||||
The Free Tier is limited to the same time frame for RDS...
|
|
||||||
If I really wanted to make this completely free I could use AWS Lambda, which is also free up to 1 million requests/month :D, to turn off both the EC2 and RDS instances at night. Gotta think when do recruiters go to sleep...
|
|
||||||
|
|
||||||
|
|
||||||
Now seriously, to actualy set this up:
|
|
||||||
|
|
||||||
Select:
|
|
||||||
burstable class db.t4g.micro
|
|
||||||
General Purpose SSD (gp3) 20GiB
|
|
||||||
I disabled autoscaling as it's not possible I would use more than this in this use-case.
|
|
||||||
|
|
||||||
Strapi recommends PostgreSQL v14.0 at the time of writing
|
|
||||||
AWS offers 14.5 above, let's choose 14.9 R1, should be backwards compatible.
|
|
||||||
Template -> Free tier
|
|
||||||
|
|
||||||
Security:
|
|
||||||
As my OpSec dictates I choose passwords with high entropy so you can't hack me.
|
|
||||||
|
|
||||||
Backup & Maintenance:
|
|
||||||
Setup Backup and Amazon auto-maintenance windows, I am not a maniac to not backup.
|
|
||||||
|
|
||||||
Connectivity:
|
|
||||||
I will not connect this to my EC2 instance, because I will be testing this on localhost.
|
|
||||||
I need public access and in this case I do not think accessing by a private network is necessary.
|
|
||||||
I will make a new VPC security group, then we can access it via an internet gateway.
|
|
||||||
|
|
||||||
|
|
||||||
https://docs.aws.amazon.com/images/AmazonRDS/latest/UserGuide/images/GS-VPC-network.png
|
|
|
@ -1,26 +1,18 @@
|
||||||
---
|
---
|
||||||
title: First post
|
title: First post
|
||||||
type: blog
|
|
||||||
excerpt: First post
|
excerpt: First post
|
||||||
|
date: 2021-01-01
|
||||||
tags:
|
tags:
|
||||||
- Blog
|
- first
|
||||||
- Linux
|
- post
|
||||||
- Declarative
|
published: true
|
||||||
image: Feature.jpg
|
image: Feature.jpg
|
||||||
postTitle: 'Best Medium Format Camera for Starting Out'
|
|
||||||
focusKeyphrase: 'best medium format camera'
|
|
||||||
datePublished: '2023-04-07T16:04:42.000+0100'
|
|
||||||
lastUpdated: '2023-04-14T10:17:52.000+0100'
|
|
||||||
seoMetaDescription: "Let's take a look."
|
|
||||||
featuredImage: ''
|
|
||||||
featuredImageAlt: 'Our own pcb'
|
|
||||||
publicImageId: ''
|
|
||||||
---
|
---
|
||||||
## Svelte
|
## Svelte
|
||||||
|
|
||||||
Media inside the **Svelte** folder is served from the `static` folder.
|
Media inside the **Svelte** folder is served from the `static` folder.
|
||||||
|
|
||||||
```python showLines
|
```python
|
||||||
|
|
||||||
input_text = ''' "yahooapis.com",
|
input_text = ''' "yahooapis.com",
|
||||||
"hotmail.com",
|
"hotmail.com",
|
||||||
|
@ -38,112 +30,3 @@ output_text = '\n'.join(formatted_lines)
|
||||||
print(output_text)
|
print(output_text)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Turborepo [see 1st source]
|
|
||||||
I will use turborepo as I have experience with it from previous projects, I prefer the centralized, more orderly handling of repositories.
|
|
||||||
Because our CMS and webapp are workspaces that need to be configured differently, we need to extend the root config of Turborepo to each app individually by creating a turbo.json in each workspace.
|
|
||||||
|
|
||||||
For Strapi this will be:
|
|
||||||
$root/apps/cms/turbo.json
|
|
||||||
|
|
||||||
``` json
|
|
||||||
|
|
||||||
{
|
|
||||||
"extends": ["//"],
|
|
||||||
"pipeline": {
|
|
||||||
"build": {
|
|
||||||
// custom configuration for the build task in this workspace
|
|
||||||
},
|
|
||||||
// new tasks only available in this workspace
|
|
||||||
"special-task": {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For Sveltekit Webapp:
|
|
||||||
$root/apps/web/turbo.json
|
|
||||||
|
|
||||||
``` json
|
|
||||||
{
|
|
||||||
"extends": ["//"],
|
|
||||||
"pipeline": {
|
|
||||||
"build": {
|
|
||||||
"outputs": [".svelte-kit/**"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Key notes about this setup:
|
|
||||||
|
|
||||||
Docker:
|
|
||||||
Base Layer: Installs container dependencies and Turborepo globally.
|
|
||||||
Pruned Layer: Copies project files and runs turbo prune to exclude unnecessary dependencies.
|
|
||||||
Installer Layer: Copies pruned workspace and runs yarn to install dependencies.
|
|
||||||
Runner Layer: Starts the app.
|
|
||||||
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
Alpine is more lightweight than Ubuntu, I will stick to the inspiration blog post [see 2nd source], which has a similar setup.
|
|
||||||
The blog post creates intermediate images 'base', 'pruned' etc. that can be used in subsequent stages.
|
|
||||||
|
|
||||||
To push images to docker hub:
|
|
||||||
|
|
||||||
```docker push mando42/portfolio:tagname```
|
|
||||||
|
|
||||||
Learned
|
|
||||||
|
|
||||||
Used sources:
|
|
||||||
|
|
||||||
1. https://turbo.build/repo/docs
|
|
||||||
2. https://dev.to/moofoo/creating-a-development-dockerfile-and-docker-composeyml-for-yarn-122-monorepos-using-turborepo-896
|
|
||||||
|
|
||||||
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
Alpine is more lightweight than Ubuntu, I will stick to the inspiration blog post [see 2nd source], which has a similar setup.
|
|
||||||
The blog post creates intermediate images 'base', 'pruned' etc. that can be used in subsequent stages.
|
|
||||||
|
|
||||||
To push images to docker hub:
|
|
||||||
|
|
||||||
```docker push mando42/portfolio:tagname```
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
Alpine is more lightweight than Ubuntu, I will stick to the inspiration blog post [see 2nd source], which has a similar setup.
|
|
||||||
The blog post creates intermediate images 'base', 'pruned' etc. that can be used in subsequent stages.
|
|
||||||
|
|
||||||
To push images to docker hub:
|
|
||||||
|
|
||||||
```docker push mando42/portfolio:tagname```
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
Alpine is more lightweight than Ubuntu, I will stick to the inspiration blog post [see 2nd source], which has a similar setup.
|
|
||||||
The blog post creates intermediate images 'base', 'pruned' etc. that can be used in subsequent stages.
|
|
||||||
|
|
||||||
To push images to docker hub:
|
|
||||||
|
|
||||||
```docker push mando42/portfolio:tagname```
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
Alpine is more lightweight than Ubuntu, I will stick to the inspiration blog post [see 2nd source], which has a similar setup.
|
|
||||||
The blog post creates intermediate images 'base', 'pruned' etc. that can be used in subsequent stages.
|
|
||||||
|
|
||||||
To push images to docker hub:
|
|
||||||
|
|
||||||
```docker push mando42/portfolio:tagname```
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
Alpine is more lightweight than Ubuntu, I will stick to the inspiration blog post [see 2nd source], which has a similar setup.
|
|
||||||
The blog post creates intermediate images 'base', 'pruned' etc. that can be used in subsequent stages.
|
|
||||||
|
|
||||||
To push images to docker hub:
|
|
||||||
|
|
||||||
```docker push mando42/portfolio:tagname```
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
Alpine is more lightweight than Ubuntu, I will stick to the inspiration blog post [see 2nd source], which has a similar setup.
|
|
||||||
The blog post creates intermediate images 'base', 'pruned' etc. that can be used in subsequent stages.
|
|
||||||
|
|
||||||
To push images to docker hub:
|
|
||||||
|
|
||||||
```docker push mando42/portfolio:tagname```
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
install Grafana, Prometheus, ELK Stack, and Jenkins on a single server and use them to monitor other EC2 instances or cloud resources. There are trade-offs:
|
|
||||||
|
|
||||||
Pros:
|
|
||||||
|
|
||||||
Simplified Management: All tools in one place.
|
|
||||||
Lower Costs: Fewer servers to maintain.
|
|
||||||
|
|
||||||
Cons:
|
|
||||||
|
|
||||||
Resource Contention: These tools can be resource-intensive.
|
|
||||||
Single Point of Failure: If the server goes down, all tools are affected.
|
|
||||||
Security Risks: Multiple services on one server can increase the attack surface.
|
|
||||||
|
|
||||||
Recommendations:
|
|
||||||
|
|
||||||
Use containerization (Docker) for easier management and isolation.
|
|
||||||
Set up a robust backup and recovery strategy.
|
|
||||||
Ensure adequate resource allocation and scaling capabilities.
|
|
|
@ -1,19 +1,12 @@
|
||||||
---
|
---
|
||||||
title: First post
|
title: w post
|
||||||
type: blog
|
excerpt: w post
|
||||||
excerpt: First post
|
date: 2021-01-01
|
||||||
tags:
|
tags:
|
||||||
- NixOS
|
- first
|
||||||
- Linux
|
- post
|
||||||
- Declarative
|
published: true
|
||||||
image: Feature.jpg
|
image: Feature.jpg
|
||||||
postTitle: 'Best Medium Format Camera for Starting Out'
|
|
||||||
focusKeyphrase: 'best medium format camera'
|
|
||||||
datePublished: '2022-04-07T16:04:42.000+0100'
|
|
||||||
lastUpdated: '2022-04-14T10:17:52.000+0100'
|
|
||||||
seoMetaDescription: "Let's take a look."
|
|
||||||
featuredImage: ''
|
|
||||||
featuredImageAlt: 'Our own pcb'
|
|
||||||
publicImageId: ''
|
|
||||||
---
|
---
|
||||||
## Svelte
|
|
||||||
|
## Svelte
|
|
@ -1,76 +0,0 @@
|
||||||
The goal of this project is to showcase my skillset both as a portfolio site and showcase how I used devops and web development practices to build it.
|
|
||||||
The tech stack and feature set is meant to:
|
|
||||||
legitimize the goal of the portfolio site, which is mentioned above
|
|
||||||
Use modern DevOps and web development practices that I know of so I can build it as fast as possible.
|
|
||||||
minimize costs and be potentially scalable and feature-upgradeable.
|
|
||||||
be secure and be build using my evolving OpSec and limited cybersecurity knowledge.
|
|
||||||
show proficiency in using new tools.
|
|
||||||
|
|
||||||
As such the current cloud stack is:
|
|
||||||
1. AWS EC2 t3.micro
|
|
||||||
For: Jenkins
|
|
||||||
Why: CI/CD pipeline, free tier, 1GB RAM sufficient for Jenkins.
|
|
||||||
2. AWS EC2 t3.micro
|
|
||||||
For: docker-compose SvelteKit app
|
|
||||||
Why: Frontend, free tier, 1GB RAM sufficient for SvelteKit.
|
|
||||||
|
|
||||||
3. Amazon ECS Anywhere
|
|
||||||
|
|
||||||
For: docker-compose Strapi
|
|
||||||
Why: Container orchestration, 2200 free hours, scalable.
|
|
||||||
|
|
||||||
4. AWS Lambda
|
|
||||||
|
|
||||||
For: Automated tasks
|
|
||||||
Why: 1 million free requests, event-driven architecture.
|
|
||||||
|
|
||||||
5. Amazon RDS
|
|
||||||
|
|
||||||
For: Database
|
|
||||||
Why: 750 free hours, managed service, 20GB storage.
|
|
||||||
|
|
||||||
6. Amazon S3
|
|
||||||
|
|
||||||
For: File storage, backups
|
|
||||||
Why: 5GB free storage, durable.
|
|
||||||
|
|
||||||
7. Amazon CloudWatch
|
|
||||||
|
|
||||||
For: Monitoring
|
|
||||||
Why: 10 free custom metrics and alarms.
|
|
||||||
|
|
||||||
8. AWS Secrets Manager
|
|
||||||
|
|
||||||
For: Secrets
|
|
||||||
Why: Secure, but consider alternatives due to cost.
|
|
||||||
|
|
||||||
9. Amazon API Gateway
|
|
||||||
|
|
||||||
For: APIs
|
|
||||||
Why: 1 million free API calls, secure.
|
|
||||||
|
|
||||||
10. Terraform and Ansible
|
|
||||||
|
|
||||||
For: IaC and Configuration
|
|
||||||
Why: Version control, automation.
|
|
||||||
|
|
||||||
11. Documentation
|
|
||||||
|
|
||||||
For: READMEs
|
|
||||||
Why: Clarity, onboarding, and best practices.
|
|
||||||
|
|
||||||
12. GitHub and AWS ECR
|
|
||||||
|
|
||||||
For: Code and container repositories
|
|
||||||
Why: Version control, Docker Hub limitations.
|
|
||||||
|
|
||||||
Cost Analysis
|
|
||||||
|
|
||||||
EC2 t3.micro: Free tier
|
|
||||||
ECS Anywhere: Free tier (2200 hours)
|
|
||||||
Lambda: Free tier (1 million requests)
|
|
||||||
RDS: Free tier (750 hours)
|
|
||||||
S3: Free tier (5GB)
|
|
||||||
CloudWatch: Free tier (10 metrics)
|
|
||||||
Secrets Manager: $0.40/secret, consider alternatives
|
|
||||||
API Gateway: Free tier (1 million calls)
|
|
|
@ -1,20 +1,13 @@
|
||||||
---
|
---
|
||||||
title: First post
|
title: Second post
|
||||||
type: blog
|
excerpt: Second post
|
||||||
excerpt: First post
|
date: 2023-01-01
|
||||||
tags:
|
tags:
|
||||||
- NixOS
|
- first
|
||||||
- Linux
|
- post
|
||||||
- Declarative
|
published: true
|
||||||
image: Feature.jpg
|
image: Feature.jpg
|
||||||
postTitle: 'Best Medium Format Camera for Starting Out'
|
|
||||||
focusKeyphrase: 'best medium format camera'
|
|
||||||
datePublished: '2024-04-07T16:04:42.000+0100'
|
|
||||||
lastUpdated: '2021-04-14T10:17:52.000+0100'
|
|
||||||
seoMetaDescription: "Let's take a look."
|
|
||||||
featuredImage: ''
|
|
||||||
featuredImageAlt: 'Our own pcb'
|
|
||||||
publicImageId: ''
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,4 +23,5 @@ Media inside the **Svelte** folder is server from the `static` folder.
|
||||||
|
|
||||||
I am ditching the societal value of having a contributions table on my profile, you should view it on git.mattmor.in
|
I am ditching the societal value of having a contributions table on my profile, you should view it on git.mattmor.in
|
||||||
|
|
||||||
--- If my contributions in 3d do not work, Github made breaking changes to their frontend and I can't scrape it anymore.
|
|
||||||
|
--- If my contributions in 3d do not work, Github made breaking changes to their frontend and I can't scrape it anymore.
|
|
@ -1,74 +0,0 @@
|
||||||
Hard skills in SW:
|
|
||||||
HTML, CSS, JS, TS, Svelte, Sveltekit, Vite,
|
|
||||||
|
|
||||||
WebDev (meta)frameworks: Svelte (and sveltekit), a theoretical knowledge of reactjs (and next.js) and vue (and nuxt)
|
|
||||||
WebDev langs: HTML, CSS, tailwindcss, postcss (basics), JS, TS (basics), multiple ui libraries and billions of packages :D
|
|
||||||
WebDev linting: eslint
|
|
||||||
WebDev testing: Playwright
|
|
||||||
|
|
||||||
Languages: JS, Python, Micropython, C and C++ (both basics with microcontrollers), I really want to code in OstraJava and Brainf*ck
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
OS:
|
|
||||||
Linux [Ubuntu, Debian (Rpi OS, Kali - basics), QubesOS, Arch (Manjaro)], WSL,
|
|
||||||
Windows :D, advanced as a power user of XP,Vista,7,8,10,11 in my life, caused deep trauma. Haven't used win servers and don't plan to.
|
|
||||||
|
|
||||||
Terminal:
|
|
||||||
Process Monitoring
|
|
||||||
Performance Monitoring
|
|
||||||
Networking Tools
|
|
||||||
Text Manipulation
|
|
||||||
|
|
||||||
Scripting:
|
|
||||||
Bash
|
|
||||||
Power Shell (for user tasks)
|
|
||||||
|
|
||||||
Editors:
|
|
||||||
Nano, Emacs, VS Code, Notepad :D and Jupyter notebook
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Version control: Git
|
|
||||||
|
|
||||||
|
|
||||||
VCS Hosting: Github, Gitlab
|
|
||||||
CI/CD:
|
|
||||||
Jenkins In progress
|
|
||||||
Gitlab CI In progress
|
|
||||||
|
|
||||||
Infrastructure Provisioning:
|
|
||||||
Terraform: In progress
|
|
||||||
|
|
||||||
Cloud Providers:
|
|
||||||
AWS - Preffered
|
|
||||||
DigitalOcean
|
|
||||||
Google Cloud (I did auth, api, company set up and some bots with spreadsheet)
|
|
||||||
|
|
||||||
CDN: Cloudinary
|
|
||||||
|
|
||||||
Networking, Security and Protocols:
|
|
||||||
FTP / SFTP
|
|
||||||
SSL / TLS
|
|
||||||
HTTP / HTTPS
|
|
||||||
DNS
|
|
||||||
SSH (putty and linux)
|
|
||||||
|
|
||||||
Serverless: AWS Lambda (0), Cloudflare, Vercel
|
|
||||||
|
|
||||||
Monorepo (with pipelines): all yarn, npm, pnpm with turborepo
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Containerization: docker (dockerfile, docker-compose, ran many apps with it), DockerHub, AWS RCD
|
|
||||||
|
|
||||||
Container Orchestration: Docker swarm(basics)
|
|
||||||
K8s/K3s: In progress...
|
|
||||||
|
|
||||||
Orchestration: Terraform (0)
|
|
||||||
|
|
||||||
GitOps: In progress ArgoCD
|
|
||||||
|
|
||||||
Application monitoring: New Relic, Prometheus, Grafana, Elk stack
|
|
||||||
|
|
||||||
Logs Management:
|
|
|
@ -1,56 +0,0 @@
|
||||||
|
|
||||||
## Turborepo [see 1st source]
|
|
||||||
I will use turborepo as I have experience with it from previous projects, I prefer the centralized, more orderly handling of repositories.
|
|
||||||
Because our CMS and webapp are workspaces that need to be configured differently, we need to extend the root config of Turborepo to each app individually by creating a turbo.json in each workspace.
|
|
||||||
|
|
||||||
For Strapi this will be:
|
|
||||||
$root/apps/cms/turbo.json
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"extends": ["//"],
|
|
||||||
"pipeline": {
|
|
||||||
"build": {
|
|
||||||
// custom configuration for the build task in this workspace
|
|
||||||
},
|
|
||||||
// new tasks only available in this workspace
|
|
||||||
"special-task": {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For Sveltekit Webapp:
|
|
||||||
$root/apps/web/turbo.json
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"extends": ["//"],
|
|
||||||
"pipeline": {
|
|
||||||
"build": {
|
|
||||||
"outputs": [".svelte-kit/**"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Key notes about this setup:
|
|
||||||
|
|
||||||
Docker:
|
|
||||||
Base Layer: Installs container dependencies and Turborepo globally.
|
|
||||||
Pruned Layer: Copies project files and runs turbo prune to exclude unnecessary dependencies.
|
|
||||||
Installer Layer: Copies pruned workspace and runs yarn to install dependencies.
|
|
||||||
Runner Layer: Starts the app.
|
|
||||||
|
|
||||||
## Docker
|
|
||||||
Alpine is more lightweight than Ubuntu, I will stick to the inspiration blog post [see 2nd source], which has a similar setup.
|
|
||||||
The blog post creates intermediate images 'base', 'pruned' etc. that can be used in subsequent stages.
|
|
||||||
|
|
||||||
To push images to docker hub:
|
|
||||||
|
|
||||||
```docker push mando42/portfolio:tagname```
|
|
||||||
|
|
||||||
|
|
||||||
Learned
|
|
||||||
|
|
||||||
Used sources:
|
|
||||||
1. https://turbo.build/repo/docs
|
|
||||||
2. https://dev.to/moofoo/creating-a-development-dockerfile-and-docker-composeyml-for-yarn-122-monorepos-using-turborepo-896
|
|
|
@ -1,40 +0,0 @@
|
||||||
.code-highlight {
|
|
||||||
position: relative;
|
|
||||||
font-family: 'Fira Mono', monospace;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
max-height: 60vh;
|
|
||||||
margin: 1rem auto;
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
max-width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-highlight > .code-language {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
border-top-right-radius: 0.5rem;
|
|
||||||
border-bottom-left-radius: 0.5rem;
|
|
||||||
background: rgba(127, 127, 127, 0.3);
|
|
||||||
padding: 0 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-highlight > code {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-highlight > code.numbered {
|
|
||||||
margin-left: 2rem;
|
|
||||||
padding-left: 0.5rem;
|
|
||||||
border-left: 1px solid rgba(127, 127, 127, 0.5);
|
|
||||||
counter-reset: line;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-highlight > code > .line-of-code {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
display: inline-block;
|
|
||||||
min-height: 1em;
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
:root {
|
|
||||||
--light: #f1f4f8;
|
|
||||||
--dark: #2c2d2f;
|
|
||||||
--primary: #ff3e00;
|
|
||||||
--fg: var(--light);
|
|
||||||
--bg: var(--dark);
|
|
||||||
--color-scrollbar: rgba(0, 0, 0, 0.3);
|
|
||||||
--shadow: 0 0.5rem 0.5rem 0 rgba(0, 0, 0, 0.2), 0 0.25rem 1rem 0 rgba(0, 0, 0, 0.2),
|
|
||||||
0 0.5rem 0.25rem -0.25rem rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--fg: var(--light);
|
|
||||||
--bg: var(--dark);
|
|
||||||
--color-scrollbar: rgba(255, 255, 255, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
body.light {
|
|
||||||
--fg: var(--dark);
|
|
||||||
--bg: var(--light);
|
|
||||||
--color-scrollbar: rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (prefers-color-scheme: light) {
|
|
||||||
:root {
|
|
||||||
--fg: var(--dark);
|
|
||||||
--bg: var(--light);
|
|
||||||
--color-scrollbar: rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
body.dark {
|
|
||||||
--fg: var(--light);
|
|
||||||
--bg: var(--dark);
|
|
||||||
--color-scrollbar: rgba(255, 255, 255, 0.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: var(--fg);
|
|
||||||
background-color: var(--bg);
|
|
||||||
font-family: 'Fira Mono', monospace;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
height: 1080px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
font-family: 'Fira Mono', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
.prose {
|
|
||||||
font-family: 'Cooper Hewitt', sans-serif;
|
|
||||||
letter-spacing: 0.25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0;
|
|
||||||
width: calc(100vw - 2rem);
|
|
||||||
max-width: 1920px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: currentColor;
|
|
||||||
transition: color 200ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: var(--color-scrollbar) transparent;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar {
|
|
||||||
width: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-thumb {
|
|
||||||
background-color: var(--color-scrollbar);
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shadow {
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
margin-bottom: 0.1rem;
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
// src/lib/cloudinary.ts
|
|
||||||
import { v2 as cloudinary } from 'cloudinary';
|
|
||||||
import { PUBLIC_CLOUDINARY_NAME } from '$env/static/public';
|
|
||||||
import { CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET } from '$env/static/private';
|
|
||||||
|
|
||||||
cloudinary.config({
|
|
||||||
cloud_name: PUBLIC_CLOUDINARY_NAME,
|
|
||||||
api_key: CLOUDINARY_API_KEY,
|
|
||||||
api_secret: CLOUDINARY_API_SECRET
|
|
||||||
});
|
|
||||||
|
|
||||||
export default cloudinary;
|
|
|
@ -1,26 +1,26 @@
|
||||||
<section class="grid grid-cols-1 md:grid-cols-3 gap-4 lg:gap-8" data-svelte-h="svelte-1u9sn7t">
|
<section class="grid grid-cols-1 md:grid-cols-3 gap-4 lg:gap-8" data-svelte-h="svelte-1u9sn7t">
|
||||||
<div class="card variant-ringed-hollow p-4 md:p-8 space-y-4">
|
<div class="card variant-ringed-hollow p-4 md:p-8 space-y-4">
|
||||||
<i class="fa-solid fa-screwdriver-wrench text-4xl text-primary-500" />
|
<i class="fa-solid fa-screwdriver-wrench text-4xl text-primary-500" />
|
||||||
<h3 class="h3">Development</h3>
|
<h3 class="h3">Development</h3>
|
||||||
<p class="opacity-75">
|
<p class="opacity-75">
|
||||||
I hone my problem solving and coding skills all the time. I try to learn the underlying
|
I possess a wide range of skills that enable me to develop visually appealing and
|
||||||
mechanics and use abstractions where relevant.
|
interactive user interfaces for web applications.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card variant-ringed-hollow p-4 md:p-8 space-y-4">
|
<div class="card variant-ringed-hollow p-4 md:p-8 space-y-4">
|
||||||
<i class="fa-solid fa-palette text-4xl text-primary-500" />
|
<i class="fa-solid fa-palette text-4xl text-primary-500" />
|
||||||
<h3 class="h3">Creativity & Presentation</h3>
|
<h3 class="h3">Design</h3>
|
||||||
<p class="opacity-75">
|
<p class="opacity-75">
|
||||||
I try to create better, novel ways to approach problems and give my best to present them
|
Over the years, I have honed my ability to create visually appealing interfaces that
|
||||||
in a clear and concise manner.
|
are both user-friendly and intuitive.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card variant-ringed-hollow p-4 md:p-8 space-y-4">
|
<div class="card variant-ringed-hollow p-4 md:p-8 space-y-4">
|
||||||
<i class="fa-solid fa-users text-4xl text-primary-500" />
|
<i class="fa-solid fa-users text-4xl text-primary-500" />
|
||||||
<h3 class="h3">Teamwork & Stakeholders</h3>
|
<h3 class="h3">User Experience</h3>
|
||||||
<p class="opacity-75">
|
<p class="opacity-75">
|
||||||
From my experience with a wide range of roles I understand the how to communication with
|
I understand the importance of creating a seamless UX for end-users. Which includes
|
||||||
stakeholders across an organization.
|
a solid understanding user behavior.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { promises as fsp } from 'fs';
|
|
||||||
|
|
||||||
export async function makeDirectory(directoryPath) {
|
|
||||||
fsp.mkdir(directoryPath, { recursive: true }, (err) => {
|
|
||||||
if (err) {
|
|
||||||
return console.error(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
import { getHighlighter } from 'shiki';
|
|
||||||
import { getTheme, loadTheme } from 'shiki-themes';
|
|
||||||
|
|
||||||
const escapeChars = {
|
|
||||||
'<': '<',
|
|
||||||
'>': '>',
|
|
||||||
'&': '&',
|
|
||||||
'{': '{',
|
|
||||||
'}': '}'
|
|
||||||
};
|
|
||||||
|
|
||||||
const escapeRE = new RegExp(`[${Object.keys(escapeChars).join('')}]`, 'g');
|
|
||||||
|
|
||||||
function escape(str) {
|
|
||||||
if (str && str.length !== 0) {
|
|
||||||
return str.replace(escapeRE, (c) => escapeChars[c]);
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function render(lines, options) {
|
|
||||||
const { fg, bg } = options;
|
|
||||||
const lineNumbers = options.showLineNumbers(lines.length, options.lang);
|
|
||||||
const lang = options.lang ? `<span class="code-language">${options.lang}</span>` : '';
|
|
||||||
return `<pre class="code-highlight" style="color: ${fg}; background-color: ${bg}">${lang}<code class="${
|
|
||||||
lineNumbers ? 'numbered' : 'simple'
|
|
||||||
}">${lines.map(lineRenderer(options)).join('\n')}\n</code></pre>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lineRenderer = (options) => (line) => {
|
|
||||||
const output = line.map(tokenRenderer(options)).join('');
|
|
||||||
const { leadingWS, content, trailingWS } = splitLeadingAndTrailingWS(output);
|
|
||||||
return `${leadingWS || ''}<span class="line-of-code">${content || ''}</span>${
|
|
||||||
trailingWS || ''
|
|
||||||
}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const tokenRenderer = (options) => (token) => {
|
|
||||||
if (!token.color || token.color.toLowerCase() === options.fg) {
|
|
||||||
return escape(token.content);
|
|
||||||
}
|
|
||||||
const { leadingWS, content, trailingWS } = splitLeadingAndTrailingWS(token.content);
|
|
||||||
if (!content) {
|
|
||||||
return leadingWS;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${leadingWS}<span style="color: ${token.color}">${escape(content)}</span>${trailingWS}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
function splitLeadingAndTrailingWS(content) {
|
|
||||||
const len = content.length;
|
|
||||||
let start = 0;
|
|
||||||
let end = len;
|
|
||||||
|
|
||||||
while (start < end && isWS(content.charAt(start))) {
|
|
||||||
start++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start === end) {
|
|
||||||
return {
|
|
||||||
leadingWS: content || ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
while (end > start && isWS(content.charAt(end - 1))) {
|
|
||||||
end--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
leadingWS: content.slice(0, start),
|
|
||||||
content: content.slice(start, end),
|
|
||||||
trailingWS: end < content.length ? content.slice(end) : ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function isWS(char) {
|
|
||||||
return char === ' ' || char === '\t';
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPlaintext(lang) {
|
|
||||||
return !lang || ['plaintext', 'txt', 'text'].indexOf(lang) !== -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultOpts = {
|
|
||||||
theme: 'nord',
|
|
||||||
fg: undefined,
|
|
||||||
bg: undefined,
|
|
||||||
showLineNumbers: (numberOfLines) => numberOfLines > 5
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function createHighlighter(opts) {
|
|
||||||
const options = { ...defaultOpts, ...opts };
|
|
||||||
|
|
||||||
if (options.theme.endsWith('.json')) {
|
|
||||||
options.theme = loadTheme(options.theme);
|
|
||||||
} else {
|
|
||||||
options.theme = getTheme(options.theme);
|
|
||||||
}
|
|
||||||
const baseSettings = (
|
|
||||||
(options.theme['tokenColors'] || []).find((x) => !x.scope) || { settings: {} }
|
|
||||||
).settings;
|
|
||||||
const colors = options.theme.colors || {};
|
|
||||||
const getThemeColor = (name) => baseSettings[name] || colors[`editor.${name}`] || colors[name];
|
|
||||||
const fg = (options.fg || getThemeColor('foreground') || '#eeeeee').toLowerCase();
|
|
||||||
const bg = (options.bg || getThemeColor('background') || '#222222').toLowerCase();
|
|
||||||
return getHighlighter(options).then(
|
|
||||||
(highlighter) => (code, lang) =>
|
|
||||||
render(
|
|
||||||
isPlaintext(lang)
|
|
||||||
? [[{ content: code }]]
|
|
||||||
: highlighter.codeToTokensBase(code, lang),
|
|
||||||
{ ...options, fg, bg, lang }
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,26 +1,48 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SkillContainer from '$lib/components/skills/SkillContainer.svelte';
|
import SkillContainer from '$lib/components/SkillContainer.svelte';
|
||||||
import HeroSection from '$lib/components/home/HeroSection.svelte';
|
import HeroSection from '$lib/components/home/HeroSection.svelte';
|
||||||
import QuickCards from '$lib/components/home/QuickCards.svelte';
|
import QuickCards from '$lib/components/home/QuickCards.svelte';
|
||||||
|
|
||||||
|
|
||||||
import { website } from '$lib/config';
|
import * as conf from '$lib/config';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Matt Morin</title>
|
<title>Matt Morin</title>
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta name="description" content={website.description} />
|
<meta name="description" content={conf.description} />
|
||||||
<meta property="og:title" content={website.siteTitle} />
|
<meta property="og:title" content={conf.title} />
|
||||||
<meta property="og:type" content="article" />
|
<meta property="og:type" content="article" />
|
||||||
<meta property="og:description" content={website.description} />
|
<meta property="og:description" content={conf.description} />
|
||||||
<meta property="og:url" content={website.url} />
|
<meta property="og:url" content={conf.url} />
|
||||||
<meta property="og:image" content="/images/profile-pic.png" />
|
<meta property="og:image" content="/images/profile-pic.png" />
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="mt-24 container h-full mx-auto flex-col justify-center items-center md:w-3/4 space-y-8">
|
<div class="mt-24 container h-full mx-auto flex-col justify-center items-center md:w-3/4">
|
||||||
|
<!-- <div class="space-y-10 mt-4 text-center flex flex-col items-center">
|
||||||
|
<h1 class="h1">I make the wheels turn.</h1>
|
||||||
|
|
||||||
|
<figure>
|
||||||
|
<section class="img-bg" />
|
||||||
|
<img
|
||||||
|
src="/images/profile-pic.png"
|
||||||
|
class="w-8 h-8 md:h-[200px] md:w-[200px]"
|
||||||
|
alt="Profile picture"
|
||||||
|
/>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
|
||||||
|
<img
|
||||||
|
src="/animations/infinity-loop-icon.svg"
|
||||||
|
alt="Icon"
|
||||||
|
class="w-16 md:w-32 lg:w-48 h-full rounded-full"
|
||||||
|
/>
|
||||||
|
<h2 class="h2">My github contributions</h2>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
<HeroSection />
|
<HeroSection />
|
||||||
|
|
||||||
<QuickCards />
|
|
||||||
|
|
||||||
<SkillContainer />
|
<SkillContainer />
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 7.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 122 B |
Binary file not shown.
Before Width: | Height: | Size: 147 B |
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
|
@ -1 +0,0 @@
|
||||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
|
|
@ -4,9 +4,8 @@ import forms from '@tailwindcss/forms';
|
||||||
import typography from '@tailwindcss/typography';
|
import typography from '@tailwindcss/typography';
|
||||||
import { skeleton } from '@skeletonlabs/tw-plugin';
|
import { skeleton } from '@skeletonlabs/tw-plugin';
|
||||||
|
|
||||||
const defineConfig: Config = {
|
export default {
|
||||||
darkMode: 'media',
|
darkMode: 'class',
|
||||||
|
|
||||||
content: [
|
content: [
|
||||||
'./src/**/*.{html,js,svelte,ts}',
|
'./src/**/*.{html,js,svelte,ts}',
|
||||||
join(require.resolve('@skeletonlabs/skeleton'), '../**/*.{html,js,svelte,ts}')
|
join(require.resolve('@skeletonlabs/skeleton'), '../**/*.{html,js,svelte,ts}')
|
||||||
|
@ -28,6 +27,4 @@ const defineConfig: Config = {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
};
|
} satisfies Config;
|
||||||
|
|
||||||
export default defineConfig;
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { purgeCss } from 'vite-plugin-tailwind-purgecss';
|
||||||
import { sveltekit } from '@sveltejs/kit/vite';
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
import { defineConfig } from 'vitest/config';
|
import { defineConfig } from 'vitest/config';
|
||||||
import { threeMinifier } from '@yushijinhun/three-minifier-rollup';
|
import { threeMinifier } from '@yushijinhun/three-minifier-rollup';
|
||||||
import { imagetools } from 'vite-imagetools';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
@ -31,8 +30,7 @@ export default defineConfig({
|
||||||
greedy: [/^hljs-/]
|
greedy: [/^hljs-/]
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
{ ...threeMinifier(), enforce: 'pre' },
|
{ ...threeMinifier(), enforce: 'pre' }
|
||||||
imagetools({ removeMetadata: true })
|
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
'process.env.VITE_BUILD_TIME': JSON.stringify(new Date().toISOString())
|
'process.env.VITE_BUILD_TIME': JSON.stringify(new Date().toISOString())
|
||||||
|
|
Loading…
Reference in New Issue