~repos /website

#astro#js#html#css

git clone https://pyrossh.dev/repos/website.git

木 Personal website of pyrossh. Built with astrojs, shiki, vite.



alchemy.run.ts



import { $ } from "bun";
import alchemy from "alchemy";
import AWS from "alchemy/aws/control";
import { AccountId } from "alchemy/aws";
const domainName = "pyrossh.dev";
const app = await alchemy("");
const Tags = [
{ Key: "Stage", Value: app.stage },
{ Key: "ProvisionedBy", Value: "Alchemy" }
]
const Comment = `ProvisionedBy Alchemy - ${app.stage}`;
// Route53 Zone
const hostedZone = await AWS.Route53.HostedZone(`hosted-zone-${app.stage}`, {
Name: domainName,
HostedZoneTags: Tags,
HostedZoneConfig: {
Comment: Comment,
},
});
// S3 Buckets with Public Access Block Configuration
const websiteBucket = await AWS.S3.Bucket(`website-bucket-${app.stage}`, {
BucketName: `pyrossh-website-${app.stage}`,
PublicAccessBlockConfiguration: {
BlockPublicAcls: true,
BlockPublicPolicy: true,
IgnorePublicAcls: true,
RestrictPublicBuckets: true
},
Tags: Tags,
});
const reposBucket = await AWS.S3.Bucket(`repos-bucket-${app.stage}`, {
BucketName: `pyrossh-repos-${app.stage}`,
PublicAccessBlockConfiguration: {
BlockPublicAcls: true,
BlockPublicPolicy: true,
IgnorePublicAcls: true,
RestrictPublicBuckets: true
},
Tags: Tags,
});
// CloudFront Origin Access Control
const s3OAC = await AWS.CloudFront.OriginAccessControl(`s3-oac-${app.stage}`, {
OriginAccessControlConfig: {
Name: `s3-oac-${app.stage}`,
OriginAccessControlOriginType: "s3",
SigningBehavior: "always",
SigningProtocol: "sigv4",
},
});
// ACM Certificate (in us-east-1 for CloudFront)
// Note: AWS Certificate Manager needs to be created separately or use ACMPCA
// For now, assuming certificate ARN will be provided or created manually
// You may need to adjust this based on your Alchemy setup
// CloudFront Functions
const htmlRedirector = await AWS.CloudFront.Function(`html-redirector-${app.stage}`, {
Name: `html_redirector_${app.stage}`,
FunctionConfig: {
Comment: "HTML redirector function",
Runtime: "cloudfront-js-2.0"
},
FunctionCode: `function handler(event) {
const request = event.request;
const uri = request.uri;
if (uri.endsWith('/')) {
request.uri += 'index.html';
} else if (!uri.startsWith('/_astro')) {
request.uri += '/index.html';
}
return request;
}`,
AutoPublish: true,
});
const gitRedirector = await AWS.CloudFront.Function(`git-redirector-${app.stage}`, {
Name: `git_redirector_${app.stage}`,
FunctionConfig: {
Comment: "Git redirector function",
Runtime: "cloudfront-js-2.0"
},
FunctionCode: `function handler(event) {
const request = event.request;
const uri = request.uri;
request.uri = "/" + request.uri.replace("/repos/", "");
return request;
}`,
AutoPublish: true,
});
// CloudFront Distribution
const s3Distribution = await AWS.CloudFront.Distribution(`s3-distribution-${app.stage}`, {
Tags: Tags,
DistributionConfig: {
Origins: [
{
DomainName: websiteBucket.RegionalDomainName,
Id: websiteBucket.RegionalDomainName,
OriginAccessControlId: s3OAC.Id,
S3OriginConfig: {
OriginAccessIdentity: ""
}
},
{
DomainName: reposBucket.RegionalDomainName,
Id: reposBucket.RegionalDomainName,
OriginAccessControlId: s3OAC.Id,
S3OriginConfig: {
OriginAccessIdentity: ""
}
}
],
Enabled: true,
IPV6Enabled: true,
Aliases: [domainName],
DefaultRootObject: "index.html",
CustomErrorResponses: [400, 403, 404, 405, 414, 416, 500, 501, 502, 503, 504].map(code => ({
ErrorCode: code,
ResponseCode: code,
ResponsePagePath: code < 500 ? "/404.html" : "/500.html"
})),
CacheBehaviors: [
{
PathPattern: "/repos/*.git/*",
AllowedMethods: ["GET", "HEAD"],
CachedMethods: ["GET", "HEAD"],
TargetOriginId: reposBucket.RegionalDomainName,
ForwardedValues: {
QueryString: false,
Cookies: {
Forward: "none"
}
},
MinTTL: 0,
DefaultTTL: 86400,
MaxTTL: 31536000,
Compress: true,
ViewerProtocolPolicy: "redirect-to-https",
FunctionAssociations: [{
EventType: "viewer-request",
FunctionARN: gitRedirector.FunctionARN
}]
},
{
PathPattern: "/*",
AllowedMethods: ["GET", "HEAD"],
CachedMethods: ["GET", "HEAD"],
TargetOriginId: websiteBucket.RegionalDomainName,
ForwardedValues: {
QueryString: true,
Cookies: {
Forward: "none"
}
},
ViewerProtocolPolicy: "redirect-to-https",
MinTTL: 0,
DefaultTTL: 3600,
MaxTTL: 86400,
FunctionAssociations: [{
EventType: "viewer-request",
FunctionARN: htmlRedirector.FunctionARN
}]
}
],
DefaultCacheBehavior: {
AllowedMethods: ["GET", "HEAD"],
CachedMethods: ["GET", "HEAD"],
TargetOriginId: websiteBucket.RegionalDomainName,
ForwardedValues: {
QueryString: true,
Cookies: {
Forward: "none"
}
},
ViewerProtocolPolicy: "redirect-to-https",
MinTTL: 0,
DefaultTTL: 3600,
MaxTTL: 86400,
FunctionAssociations: [{
EventType: "viewer-request",
FunctionARN: htmlRedirector.FunctionARN
}]
},
PriceClass: "PriceClass_All",
Restrictions: {
GeoRestriction: {
RestrictionType: "none"
}
},
// resource "aws_acm_certificate" "domain_ssl_certificate" {
// domain_name = "pyrossh.dev"
// validation_method = "EMAIL"
// provider = aws.useast
// }
ViewerCertificate: {
MinimumProtocolVersion: "TLSv1.2_2021",
CloudFrontDefaultCertificate: false,
SslSupportMethod: "sni-only",
AcmCertificateArn: "arn:aws:acm:us-east-1:122129753516:certificate/d04cd0a5-623c-4d18-a9d1-f0d8df3e999d",
// AcmCertificateArn: domainSslCertificate.CertificateArn,
},
Comment: Comment,
}
});
// S3 Bucket Policies
const websiteBucketPolicy = await AWS.S3.BucketPolicy(`website-bucket-policy-${app.stage}`, {
Bucket: websiteBucket.BucketName || `pyrossh-website-${app.stage}`,
PolicyDocument: {
Version: "2012-10-17",
Statement: [{
Principal: {
Service: "cloudfront.amazonaws.com"
},
Action: "s3:GetObject",
Resource: [
websiteBucket.Arn,
`${websiteBucket.Arn}/*`
],
Effect: "Allow",
Condition: {
StringEquals: {
"AWS:SourceArn": `arn:aws:cloudfront::${await AccountId()}:distribution/${s3Distribution.Id}`
}
}
}]
}
});
const reposBucketPolicy = await AWS.S3.BucketPolicy(`repos-bucket-policy-${app.stage}`, {
Bucket: reposBucket.BucketName!,
PolicyDocument: {
Version: "2012-10-17",
Statement: [{
Principal: {
Service: "cloudfront.amazonaws.com"
},
Action: "s3:GetObject",
Resource: [
reposBucket.Arn,
`${reposBucket.Arn}/*`
],
Effect: "Allow",
Condition: {
StringEquals: {
"AWS:SourceArn": `arn:aws:cloudfront::${await AccountId()}:distribution/${s3Distribution.Id}`
}
}
}]
}
});
// Route53 A Record
// console.log({
// HostedZoneId: hostedZone.Id,
// Name: domainName,
// Type: "A",
// AliasTarget: {
// DNSName: s3Distribution.DomainName,
// HostedZoneId: s3Distribution.DomainName, // CloudFront hosted zone ID
// EvaluateTargetHealth: false
// }
// })
// const aRecordDomain = await AWS.Route53.RecordSet("a-record-domain", {
// HostedZoneId: hostedZone.Id,
// Name: domainName,
// Type: "A",
// AliasTarget: {
// DNSName: s3Distribution.DomainName,
// HostedZoneId: s3Distribution.DomainName, // CloudFront hosted zone ID
// EvaluateTargetHealth: false
// }
// });
await app.finalize();
await $`bun run build:resume:pdf`
await $`bun run build:resume:svg`
await $`rm -rf dist`
await $`bun run build`
await $`aws s3 sync --delete ./dist/ s3://${websiteBucket.BucketName}`
await $`aws cloudfront create-invalidation --distribution-id ${s3Distribution.Id} --paths "/*" --no-cli-pager`
await $`rm -rf dist`