website
git clone https://git.pyrossh.dev/website
木 Personal website of pyrossh. Built with astrojs, shiki, vite.
831f1f7
— pyrossh
2025-06-06T21:41:03+05:30
fix readmes
Generated files not shown: bun.lock
- astro.config.mjs +1 -0
- infra/main.tf +32 -18
- package.json +9 -7
- server.js +128 -0
- src/components/Commit.astro +2 -1
- src/content.config.ts +22 -12
- src/layouts/Base.astro +1 -1
- src/pages/index.module.css +11 -5
- src/pages/repos/[...slug]/index.astro +5 -16
- src/pages/repos/[...slug]/index.module.css +1 -2
astro.config.mjs
CHANGED
|
@@ -13,6 +13,7 @@ export default defineConfig({
|
|
|
13
13
|
sitemap(),
|
|
14
14
|
],
|
|
15
15
|
experimental: {
|
|
16
|
+
// csp: true,
|
|
16
17
|
fonts: [{
|
|
17
18
|
provider: fontProviders.google(),
|
|
18
19
|
name: "Roboto",
|
infra/main.tf
CHANGED
|
@@ -36,23 +36,9 @@ resource "aws_s3_bucket" "website" {
|
|
|
36
36
|
bucket = "pyrossh-website"
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
resource "aws_s3_bucket" "repos_bucket" {
|
|
40
|
-
# bucket = aws_s3_bucket.website.id
|
|
41
|
-
# acl = "private"
|
|
42
|
-
# }
|
|
43
|
-
|
|
44
|
-
# resource "aws_s3_bucket_website_configuration" "s3_bucket" {
|
|
45
|
-
# bucket = aws_s3_bucket.website.id
|
|
46
|
-
|
|
47
|
-
# index_document {
|
|
48
|
-
# suffix = "index.html"
|
|
49
|
-
# }
|
|
50
|
-
|
|
51
|
-
# error_document {
|
|
52
|
-
|
|
40
|
+
bucket = "pyrossh-repos"
|
|
53
|
-
# }
|
|
54
|
-
|
|
55
|
-
|
|
41
|
+
}
|
|
56
42
|
|
|
57
43
|
resource "aws_s3_bucket_public_access_block" "website_public_access" {
|
|
58
44
|
bucket = aws_s3_bucket.website.id
|
|
@@ -83,7 +69,9 @@ data "aws_iam_policy_document" "cloudfront_oac_access" {
|
|
|
83
69
|
|
|
84
70
|
resources = [
|
|
85
71
|
aws_s3_bucket.website.arn,
|
|
86
|
-
"${aws_s3_bucket.website.arn}/*"
|
|
72
|
+
"${aws_s3_bucket.website.arn}/*",
|
|
73
|
+
aws_s3_bucket.repos_bucket,
|
|
74
|
+
"${aws_s3_bucket.repos_bucket.arn}/*"
|
|
87
75
|
]
|
|
88
76
|
|
|
89
77
|
condition {
|
|
@@ -131,6 +119,11 @@ resource "aws_cloudfront_distribution" "s3_distribution" {
|
|
|
131
119
|
origin_id = aws_s3_bucket.website.bucket_regional_domain_name
|
|
132
120
|
origin_access_control_id = aws_cloudfront_origin_access_control.website.id
|
|
133
121
|
}
|
|
122
|
+
origin {
|
|
123
|
+
domain_name = aws_s3_bucket.repos_bucket.bucket_regional_domain_name
|
|
124
|
+
origin_id = aws_s3_bucket.repos_bucket.bucket_regional_domain_name
|
|
125
|
+
origin_access_control_id = aws_cloudfront_origin_access_control.website.id
|
|
126
|
+
}
|
|
134
127
|
enabled = true
|
|
135
128
|
is_ipv6_enabled = true
|
|
136
129
|
aliases = ["pyrossh.dev"]
|
|
@@ -164,6 +157,27 @@ resource "aws_cloudfront_distribution" "s3_distribution" {
|
|
|
164
157
|
function_arn = aws_cloudfront_function.subdirectory_redirector.arn
|
|
165
158
|
}
|
|
166
159
|
}
|
|
160
|
+
|
|
161
|
+
ordered_cache_behavior {
|
|
162
|
+
path_pattern = "/d/*"
|
|
163
|
+
allowed_methods = ["GET", "HEAD"]
|
|
164
|
+
cached_methods = ["GET", "HEAD"]
|
|
165
|
+
target_origin_id = aws_s3_bucket.repos_bucket.bucket_regional_domain_name
|
|
166
|
+
|
|
167
|
+
forwarded_values {
|
|
168
|
+
query_string = false
|
|
169
|
+
cookies {
|
|
170
|
+
forward = "none"
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
min_ttl = 0
|
|
175
|
+
default_ttl = 86400
|
|
176
|
+
max_ttl = 31536000
|
|
177
|
+
compress = true
|
|
178
|
+
viewer_protocol_policy = "redirect-to-https"
|
|
179
|
+
}
|
|
180
|
+
|
|
167
181
|
price_class = "PriceClass_All"
|
|
168
182
|
restrictions {
|
|
169
183
|
geo_restriction {
|
package.json
CHANGED
|
@@ -5,20 +5,22 @@
|
|
|
5
5
|
"scripts": {
|
|
6
6
|
"tauri": "tauri",
|
|
7
7
|
"dev": "astro dev",
|
|
8
|
-
"build": "astro build"
|
|
8
|
+
"build": "astro build",
|
|
9
|
+
"android": "tauri android dev",
|
|
10
|
+
"ios": "tauri ios dev"
|
|
9
11
|
},
|
|
10
12
|
"dependencies": {
|
|
11
13
|
"@appzic/astro-reset-css": "^1.2.0",
|
|
12
|
-
"@astrojs/mdx": "^4.
|
|
14
|
+
"@astrojs/mdx": "^4.3.0",
|
|
13
|
-
"@astrojs/rss": "^4.0.
|
|
15
|
+
"@astrojs/rss": "^4.0.12",
|
|
14
|
-
"@astrojs/sitemap": "^3.
|
|
16
|
+
"@astrojs/sitemap": "^3.4.1",
|
|
15
|
-
"astro": "^5.
|
|
17
|
+
"astro": "^5.9.0",
|
|
16
18
|
"simple-git": "^3.27.0",
|
|
17
19
|
"timeago.js": "^4.0.2"
|
|
18
20
|
},
|
|
19
21
|
"devDependencies": {
|
|
20
|
-
"@tauri-apps/cli": "^2",
|
|
21
22
|
"@playwright/test": "^1.52.0",
|
|
23
|
+
"@tauri-apps/cli": "^2",
|
|
22
24
|
"@types/node": "^22.15.3"
|
|
23
25
|
}
|
|
24
|
-
}
|
|
26
|
+
}
|
server.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Parser } from 'node-sql-parser';
|
|
2
|
+
import { PutObjectCommand, S3Client, GetObjectCommand, paginateListObjectsV2 } from "@aws-sdk/client-s3";
|
|
3
|
+
|
|
4
|
+
const bucketName = "";
|
|
5
|
+
|
|
6
|
+
// import { S3Client } from "bun";
|
|
7
|
+
// const client = new S3Client({
|
|
8
|
+
// region: "ap-south-1",
|
|
9
|
+
// bucket: "",
|
|
10
|
+
// accessKeyId: "",
|
|
11
|
+
// secretAccessKey: "",
|
|
12
|
+
// endpoint: "https://s3express-aps1-az1.ap-south-1.amazonaws.com",
|
|
13
|
+
// });
|
|
14
|
+
// const s3file = client.file("123.json");
|
|
15
|
+
// await s3file.write(JSON.stringify({ hello: "world" }));
|
|
16
|
+
|
|
17
|
+
// const data = await s3file.json()
|
|
18
|
+
// console.log(data);
|
|
19
|
+
|
|
20
|
+
// const client = new S3Client({});
|
|
21
|
+
// const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
22
|
+
// for (const i of arr) {
|
|
23
|
+
// await client.send(new PutObjectCommand({
|
|
24
|
+
// Bucket: bucketName,
|
|
25
|
+
// Key: `posts/${i}.json`,
|
|
26
|
+
// ContentType: "application/json",
|
|
27
|
+
// Body: JSON.stringify({ id: `${i}`, data: `hello '${i}' world` }),
|
|
28
|
+
// }));
|
|
29
|
+
// }
|
|
30
|
+
|
|
31
|
+
// If-None-Match: * if it doesn't exist 409 - ConditionalRequestConflict 412 Precondition Failed
|
|
32
|
+
// If-Match: etag checks if etag is same CAS 409 - ConditionalRequestConflict 412 Precondition Failed
|
|
33
|
+
|
|
34
|
+
const getObjectName = (name) => name.split("::")[2];
|
|
35
|
+
|
|
36
|
+
const client = new S3Client({});
|
|
37
|
+
|
|
38
|
+
const getAllS3Files = async (bucketName, tableName) => {
|
|
39
|
+
const totalFiles = [];
|
|
40
|
+
for await (const data of paginateListObjectsV2({ client }, { Bucket: bucketName, Prefix: `${tableName}/` })) {
|
|
41
|
+
totalFiles.push(...(data.Contents ?? []));
|
|
42
|
+
}
|
|
43
|
+
return totalFiles;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const sql = async (query) => {
|
|
47
|
+
const parser = new Parser();
|
|
48
|
+
const { tableList, columnList, ast } = parser.parse(query, {
|
|
49
|
+
database: "Hive",
|
|
50
|
+
// type: 'table',
|
|
51
|
+
parseOptions: {
|
|
52
|
+
includeLocations: true
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
// console.log(ast);
|
|
56
|
+
// console.log(JSON.stringify(ast, 2, 2));
|
|
57
|
+
// console.log("tableName", tableName);
|
|
58
|
+
const tableName = getObjectName(tableList[0]);
|
|
59
|
+
// if (ast.type === "select") {
|
|
60
|
+
// }
|
|
61
|
+
const contents = await getAllS3Files(bucketName, tableName)
|
|
62
|
+
return await Promise.all(contents.map((r) => {
|
|
63
|
+
return client.send(new GetObjectCommand({ Bucket: bucketName, Key: r.Key, }))
|
|
64
|
+
.then((rr) => rr.Body.transformToString())
|
|
65
|
+
.then(JSON.parse)
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const rows = await sql('SELECT * FROM posts LIMIT 10');
|
|
70
|
+
// const rows = await sql("INSERT INTO posts(id, data) VALUES ('11', 'body');");
|
|
71
|
+
console.log(rows);
|
|
72
|
+
|
|
73
|
+
// const rows = await sql(`
|
|
74
|
+
// CREATE TABLE struct_test(
|
|
75
|
+
// property_id INT
|
|
76
|
+
// )
|
|
77
|
+
// `);
|
|
78
|
+
|
|
79
|
+
// console.log(rows);
|
|
80
|
+
// const resp = await parseQuery("SELECT * FROM POSTS");
|
|
81
|
+
// resp.stmts[0].stmt.SelectStmt.fromClause
|
|
82
|
+
// console.log("stmts", stmts);
|
|
83
|
+
|
|
84
|
+
// Data Definition Language (DDL): CREATE, ALTER, DROP
|
|
85
|
+
// Data Manipulation Language (DML): INSERT, UPDATE, DELETE, MERGE
|
|
86
|
+
// Data Query Language (DQL): SELECT
|
|
87
|
+
// Data Control Language (DCL): GRANT, REVOKE
|
|
88
|
+
|
|
89
|
+
// TABLES/VIEWS/INDEX
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
//
|
|
94
|
+
// import { gluesql } from 'gluesql';
|
|
95
|
+
|
|
96
|
+
// const db = gluesql();
|
|
97
|
+
|
|
98
|
+
// async function run() {
|
|
99
|
+
// await db.query(`
|
|
100
|
+
// CREATE TABLE User (id INTEGER, name TEXT);
|
|
101
|
+
// CREATE TABLE Device (name TEXT, userId INTEGER);
|
|
102
|
+
// INSERT INTO User VALUES
|
|
103
|
+
// (1, 'glue'), (2, 'sticky'), (3, 'watt');
|
|
104
|
+
// INSERT INTO Device VALUES
|
|
105
|
+
// ('Phone', 1), ('Mic', 1), ('Monitor', 3),
|
|
106
|
+
// ('Mouse', 2), ('Touchpad', 2);
|
|
107
|
+
// `);
|
|
108
|
+
|
|
109
|
+
// let sql;
|
|
110
|
+
|
|
111
|
+
// sql = 'SHOW TABLES;';
|
|
112
|
+
// const [{ tables }] = await db.query(sql);
|
|
113
|
+
// console.log(`\n[Query]\n${sql}`);
|
|
114
|
+
// console.table(tables);
|
|
115
|
+
|
|
116
|
+
// sql = `
|
|
117
|
+
// SELECT
|
|
118
|
+
// u.name as user,
|
|
119
|
+
// d.name as device
|
|
120
|
+
// FROM User u
|
|
121
|
+
// JOIN Device d ON u.id = d.userId
|
|
122
|
+
// `.trim().replace(/[ ]{4}/g, '');
|
|
123
|
+
// const [{ rows }] = await db.query(sql);
|
|
124
|
+
// console.log(`\n[Query]\n${sql}`);
|
|
125
|
+
// console.table(rows);
|
|
126
|
+
// }
|
|
127
|
+
|
|
128
|
+
// run();
|
src/components/Commit.astro
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { format } from "timeago.js";
|
|
3
3
|
|
|
4
|
-
const { title, author_name, hash, date, message } = Astro.props;
|
|
4
|
+
const { title, author_name, hash, date, message, refs } = Astro.props;
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<div class="event">
|
|
@@ -11,6 +11,7 @@ const { title, author_name, hash, date, message } = Astro.props;
|
|
|
11
11
|
</a>
|
|
12
12
|
— {author_name}
|
|
13
13
|
<small class="pull-right">
|
|
14
|
+
<h3>{refs}</h3>
|
|
14
15
|
<a href={`/repos/${title}/logs?from=??`} rel="nofollow">
|
|
15
16
|
<span title={date}>
|
|
16
17
|
{format(date)}
|
src/content.config.ts
CHANGED
|
@@ -1,24 +1,38 @@
|
|
|
1
|
-
import fs from "fs";
|
|
1
|
+
import fs from "fs/promises";
|
|
2
2
|
import { simpleGit } from "simple-git";
|
|
3
3
|
import { glob } from 'astro/loaders';
|
|
4
4
|
import { defineCollection, z } from 'astro:content';
|
|
5
5
|
import { REPOS } from './consts';
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
// https://pyrossh.dev/downloads/rp2350/refs/heads/master/repo.zip
|
|
9
|
+
// https://pyrossh-repos.s3.ap-south-1.amazonaws.com/rp2350/refs/heads/master/repo.zip
|
|
10
|
+
|
|
11
|
+
async function checkFileExists(filePath: string) {
|
|
12
|
+
try {
|
|
13
|
+
await fs.stat(filePath);
|
|
14
|
+
return true; // File exists
|
|
15
|
+
} catch (error) {
|
|
16
|
+
return false; // File does not exist or cannot be accessed
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
export const collections = {
|
|
8
21
|
repos: defineCollection({
|
|
9
22
|
loader: {
|
|
10
23
|
name: "loader",
|
|
11
|
-
load: async (
|
|
24
|
+
load: async ({ renderMarkdown, store }) => {
|
|
12
25
|
for (const r of REPOS) {
|
|
13
26
|
const repoPath = `/Users/pyrossh/Code/pyrossh/${r.title}`;
|
|
14
27
|
const readmePath = repoPath + "/README.md";
|
|
15
28
|
console.log("loading repo " + repoPath)
|
|
16
29
|
const git = simpleGit(repoPath)
|
|
17
|
-
const commits = await git.log();
|
|
18
|
-
const tags = await git.tag();
|
|
19
|
-
const
|
|
30
|
+
const commits = await git.log(["--branches", "--tags"]);
|
|
20
|
-
|
|
31
|
+
const readmeContent = await checkFileExists(readmePath) ? await renderMarkdown(await fs.readFile(readmePath, 'utf-8')) : {
|
|
32
|
+
html: '',
|
|
33
|
+
metadata: {},
|
|
34
|
+
}
|
|
21
|
-
|
|
35
|
+
store.set({
|
|
22
36
|
id: r.title,
|
|
23
37
|
data: {
|
|
24
38
|
title: r.title,
|
|
@@ -31,12 +45,8 @@ export const collections = {
|
|
|
31
45
|
date: item.date,
|
|
32
46
|
refs: item.refs,
|
|
33
47
|
})),
|
|
34
|
-
branches: branches.all,
|
|
35
|
-
tags: tags,
|
|
36
48
|
},
|
|
37
|
-
rendered:
|
|
49
|
+
rendered: readmeContent,
|
|
38
|
-
html: '', // fs.existsSync(readmePath) ? await (await import(readmePath)).compiledContent() : "",
|
|
39
|
-
}
|
|
40
50
|
});
|
|
41
51
|
}
|
|
42
52
|
},
|
src/layouts/Base.astro
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
+
import "./Base.css"; // Note: This has to be first always
|
|
2
3
|
import { ClientRouter } from "astro:transitions";
|
|
3
4
|
import { Font } from "astro:assets";
|
|
4
5
|
import ResetCSS from "@appzic/astro-reset-css";
|
|
5
6
|
import Header from "@/components/Header.astro";
|
|
6
7
|
import Footer from "@/components/Footer.astro";
|
|
7
8
|
import Wrapper from "@/components/Wrapper.astro";
|
|
8
|
-
import "./Base.css";
|
|
9
9
|
|
|
10
10
|
interface Props {
|
|
11
11
|
title: string;
|
src/pages/index.module.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
h2 {
|
|
2
|
-
font-size:
|
|
2
|
+
font-size: 16pt;
|
|
3
3
|
font-weight: 600;
|
|
4
4
|
}
|
|
5
5
|
|
|
@@ -35,13 +35,19 @@ section {
|
|
|
35
35
|
|
|
36
36
|
.title {
|
|
37
37
|
font-size: 1.875rem;
|
|
38
|
-
/* text-3xl */
|
|
39
38
|
line-height: 2.25rem;
|
|
40
|
-
/* text-3xl */
|
|
41
39
|
font-weight: 700;
|
|
42
|
-
/* font-bold */
|
|
43
40
|
margin-bottom: 1rem;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.firstSection {
|
|
44
|
+
display: flex;
|
|
44
|
-
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.firstPara {
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: row;
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
.linkUnderline {
|
src/pages/repos/[...slug]/index.astro
CHANGED
|
@@ -39,30 +39,19 @@ const { Content } = await render(Astro.props);
|
|
|
39
39
|
</h3>
|
|
40
40
|
<h3>
|
|
41
41
|
wget
|
|
42
|
-
<a
|
|
42
|
+
<a href={`https://pyrossh.dev/zips/${title}.zip`}>
|
|
43
|
+
https://pyrossh.dev/zips/{title}.zip
|
|
43
|
-
|
|
44
|
+
<!-- https://pyrossh-repos.s3.ap-south-1.amazonaws.com/rp2350/refs/heads/master/repo.zip -->
|
|
44
|
-
>
|
|
45
|
-
https://pyrossh-repos.s3.ap-south-1.amazonaws.com/{
|
|
46
|
-
title
|
|
47
|
-
}/refs/heads/master/repo.zip
|
|
48
45
|
</a>
|
|
49
46
|
</h3>
|
|
50
47
|
</div>
|
|
51
48
|
<div class={styles.nav}>
|
|
52
49
|
<div>
|
|
53
|
-
<a href={`${title}/commits`}>Commits
|
|
50
|
+
<a href={`/repos/${title}/commits`}>Commits</a>
|
|
54
51
|
</div>
|
|
55
52
|
<div>|</div>
|
|
56
53
|
<div>
|
|
57
|
-
<a class="nav-link" href={`/${title}/files`}>Files</a>
|
|
54
|
+
<a class="nav-link" href={`/repos/${title}/files`}>Files</a>
|
|
58
|
-
</div>
|
|
59
|
-
<div>|</div>
|
|
60
|
-
<div>
|
|
61
|
-
<a class="nav-link" href={`/${title}/branches`}>Branches</a>
|
|
62
|
-
</div>
|
|
63
|
-
<div>|</div>
|
|
64
|
-
<div>
|
|
65
|
-
<a class="nav-link" href={`/${title}/tags`}>Tags</a>
|
|
66
55
|
</div>
|
|
67
56
|
</div>
|
|
68
57
|
<div class={styles.summary}>
|
src/pages/repos/[...slug]/index.module.css
CHANGED
|
@@ -57,9 +57,8 @@
|
|
|
57
57
|
a {
|
|
58
58
|
font-size: 12pt;
|
|
59
59
|
padding-right: 0.5rem;
|
|
60
|
-
color: #
|
|
60
|
+
color: #3395ff;
|
|
61
61
|
cursor: pointer;
|
|
62
|
-
color: #ced4da;
|
|
63
62
|
}
|
|
64
63
|
}
|
|
65
64
|
|