Merge branch 'release/1.1.6' into main

This commit is contained in:
Sergey Yarkov 2022-11-06 18:09:15 +03:00
commit 94e9effbb1
No known key found for this signature in database
GPG Key ID: E91CE1FABC619B6F
12 changed files with 292 additions and 26 deletions

4
.env.example Normal file
View File

@ -0,0 +1,4 @@
IS_SITE_UNDER_CONSTRUCTION=false
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=12345

2
.gitignore vendored
View File

@ -16,3 +16,5 @@ pnpm-debug.log*
# macOS-specific files # macOS-specific files
.DS_Store .DS_Store
# Local Netlify folder
.netlify

View File

@ -1,3 +1,10 @@
[build] [build]
command = "npm run build" command = "npm run build"
publish = "dist" publish = "dist"
functions = "netlify/functions"
[[redirects]]
from = "/api/*" # simplify all calls to serverless functions
to = "/.netlify/functions/:splat" # all function calls will go to this path
status = 200 # ok code
force = true # ensure to always redirect

View File

@ -0,0 +1,42 @@
import { Handler } from "@netlify/functions";
import Redis from "ioredis";
const redis = new Redis({
host: process.env.REDIS_HOST,
port: Number.parseInt(process.env.REDIS_PORT || "6379", 10),
password: process.env.REDIS_PASSWORD,
});
const getViewsBySlug = async (slug: string): Promise<number> => {
let views = 1;
const data = await redis.get(`views:${slug}`);
if (data) views = Number.parseInt(data, 10);
if (Number.isNaN(views)) views = 1;
return views;
};
const handler: Handler = async (event) => {
const slug = event.queryStringParameters?.slug;
switch (event.httpMethod) {
case "GET": {
if (!slug) return { statusCode: 400, body: "Bad request." };
const views = await getViewsBySlug(slug);
return { statusCode: 200, body: views.toString() };
}
case "POST": {
if (!slug) return { statusCode: 400, body: "Bad request." };
const views = await redis.incr(`views:${slug}`);
return { statusCode: 200, body: views.toString() };
}
default: {
return {
statusCode: 500,
body: "Unrecognized HTTP Method, must be one of GET/POST",
};
}
}
};
export { handler };

180
package-lock.json generated
View File

@ -1,14 +1,16 @@
{ {
"name": "yarkov.tech", "name": "yarkov.tech",
"version": "1.0.3", "version": "1.0.6",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "yarkov.tech", "name": "yarkov.tech",
"version": "1.0.3", "version": "1.0.6",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@netlify/functions": "^1.3.0",
"ioredis": "^5.2.4",
"sharp": "^0.31.2" "sharp": "^0.31.2"
}, },
"devDependencies": { "devDependencies": {
@ -798,6 +800,11 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true "dev": true
}, },
"node_modules/@ioredis/commands": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
},
"node_modules/@jridgewell/gen-mapping": { "node_modules/@jridgewell/gen-mapping": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
@ -927,6 +934,17 @@
"rollup": ">=2" "rollup": ">=2"
} }
}, },
"node_modules/@netlify/functions": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-1.3.0.tgz",
"integrity": "sha512-hN/Fgpz8XIOBfsBPLYUMxVKBlCopgeqGB0popayicnmkFLnvKByTTMYgF01wcF9DBtBQdV0H2h1kPFpMl34I8w==",
"dependencies": {
"is-promise": "^4.0.0"
},
"engines": {
"node": ">=8.3.0"
}
},
"node_modules/@node-minify/core": { "node_modules/@node-minify/core": {
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/@node-minify/core/-/core-5.3.0.tgz", "resolved": "https://registry.npmjs.org/@node-minify/core/-/core-5.3.0.tgz",
@ -2333,6 +2351,14 @@
"node": ">=0.8" "node": ">=0.8"
} }
}, },
"node_modules/cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/color": { "node_modules/color": {
"version": "4.2.3", "version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@ -2485,7 +2511,6 @@
"version": "4.3.4", "version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": { "dependencies": {
"ms": "2.1.2" "ms": "2.1.2"
}, },
@ -2591,6 +2616,14 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"engines": {
"node": ">=0.10"
}
},
"node_modules/dequal": { "node_modules/dequal": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@ -4796,6 +4829,29 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/ioredis": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.2.4.tgz",
"integrity": "sha512-qIpuAEt32lZJQ0XyrloCRdlEdUUNGG9i0UOk6zgzK6igyudNWqEBxfH6OlbnOOoBBvr1WB02mm8fR55CnikRng==",
"dependencies": {
"@ioredis/commands": "^1.1.1",
"cluster-key-slot": "^1.1.0",
"debug": "^4.3.4",
"denque": "^2.0.1",
"lodash.defaults": "^4.2.0",
"lodash.isarguments": "^3.1.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0",
"standard-as-callback": "^2.1.0"
},
"engines": {
"node": ">=12.22.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/ioredis"
}
},
"node_modules/is-alphabetical": { "node_modules/is-alphabetical": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
@ -5070,6 +5126,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/is-promise": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="
},
"node_modules/is-reference": { "node_modules/is-reference": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.0.tgz", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.0.tgz",
@ -5404,6 +5465,16 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
},
"node_modules/lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -6735,8 +6806,7 @@
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"dev": true
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.4", "version": "3.3.4",
@ -7590,6 +7660,25 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/redis-errors": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
"engines": {
"node": ">=4"
}
},
"node_modules/redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
"dependencies": {
"redis-errors": "^1.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/regexp.prototype.flags": { "node_modules/regexp.prototype.flags": {
"version": "1.4.3", "version": "1.4.3",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
@ -8472,6 +8561,11 @@
"deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility",
"dev": true "dev": true
}, },
"node_modules/standard-as-callback": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="
},
"node_modules/string_decoder": { "node_modules/string_decoder": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@ -10663,6 +10757,11 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true "dev": true
}, },
"@ioredis/commands": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
},
"@jridgewell/gen-mapping": { "@jridgewell/gen-mapping": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
@ -10771,6 +10870,14 @@
"vfile": "^5.0.0" "vfile": "^5.0.0"
} }
}, },
"@netlify/functions": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-1.3.0.tgz",
"integrity": "sha512-hN/Fgpz8XIOBfsBPLYUMxVKBlCopgeqGB0popayicnmkFLnvKByTTMYgF01wcF9DBtBQdV0H2h1kPFpMl34I8w==",
"requires": {
"is-promise": "^4.0.0"
}
},
"@node-minify/core": { "@node-minify/core": {
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/@node-minify/core/-/core-5.3.0.tgz", "resolved": "https://registry.npmjs.org/@node-minify/core/-/core-5.3.0.tgz",
@ -11818,6 +11925,11 @@
"integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
"dev": true "dev": true
}, },
"cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="
},
"color": { "color": {
"version": "4.2.3", "version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@ -11944,7 +12056,6 @@
"version": "4.3.4", "version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"requires": { "requires": {
"ms": "2.1.2" "ms": "2.1.2"
} }
@ -12014,6 +12125,11 @@
"object-keys": "^1.1.1" "object-keys": "^1.1.1"
} }
}, },
"denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="
},
"dequal": { "dequal": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@ -13577,6 +13693,22 @@
"side-channel": "^1.0.4" "side-channel": "^1.0.4"
} }
}, },
"ioredis": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.2.4.tgz",
"integrity": "sha512-qIpuAEt32lZJQ0XyrloCRdlEdUUNGG9i0UOk6zgzK6igyudNWqEBxfH6OlbnOOoBBvr1WB02mm8fR55CnikRng==",
"requires": {
"@ioredis/commands": "^1.1.1",
"cluster-key-slot": "^1.1.0",
"debug": "^4.3.4",
"denque": "^2.0.1",
"lodash.defaults": "^4.2.0",
"lodash.isarguments": "^3.1.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0",
"standard-as-callback": "^2.1.0"
}
},
"is-alphabetical": { "is-alphabetical": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
@ -13743,6 +13875,11 @@
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true "dev": true
}, },
"is-promise": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="
},
"is-reference": { "is-reference": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.0.tgz", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.0.tgz",
@ -13988,6 +14125,16 @@
"p-locate": "^5.0.0" "p-locate": "^5.0.0"
} }
}, },
"lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
},
"lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="
},
"lodash.merge": { "lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -14889,8 +15036,7 @@
"ms": { "ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"dev": true
}, },
"nanoid": { "nanoid": {
"version": "3.3.4", "version": "3.3.4",
@ -15485,6 +15631,19 @@
} }
} }
}, },
"redis-errors": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="
},
"redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
"requires": {
"redis-errors": "^1.0.0"
}
},
"regexp.prototype.flags": { "regexp.prototype.flags": {
"version": "1.4.3", "version": "1.4.3",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
@ -16131,6 +16290,11 @@
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
"dev": true "dev": true
}, },
"standard-as-callback": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="
},
"string_decoder": { "string_decoder": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",

View File

@ -1,6 +1,6 @@
{ {
"name": "yarkov.tech", "name": "yarkov.tech",
"version": "1.0.6", "version": "1.1.6",
"private": true, "private": true,
"description": "Personal website created with Astro.", "description": "Personal website created with Astro.",
"author": { "author": {
@ -47,6 +47,8 @@
"solid-js": "^1.6.1" "solid-js": "^1.6.1"
}, },
"dependencies": { "dependencies": {
"@netlify/functions": "^1.3.0",
"ioredis": "^5.2.4",
"sharp": "^0.31.2" "sharp": "^0.31.2"
} }
} }

View File

@ -1,10 +1,12 @@
--- ---
import { Picture } from "@astrojs/image/components"; import { Picture } from "@astrojs/image/components";
import { Icon } from "astro-icon";
import { createDateFormatter } from "@root/utils"; import { createDateFormatter } from "@root/utils";
import { isURL } from "@utils/string"; import { isURL } from "@utils/string";
import ArticleComments from "@components/ArticleComments"; import ArticleComments from "@components/ArticleComments";
import TranslatedLink from "../TranslatedLink"; import TranslatedLink from "../TranslatedLink";
import "./ArticlePost.scss"; import "./ArticlePost.scss";
import { useTranslation } from "@root/i18n/utils";
export type Props = { export type Props = {
data: ArticleType; data: ArticleType;
@ -12,23 +14,30 @@ export type Props = {
}; };
const { data, lang } = Astro.props as Props; const { data, lang } = Astro.props as Props;
const { title, description, coverImage, tags, author, published_at } = data; const { slug, title, description, coverImage, tags, author, published_at } = data;
const t = useTranslation(Astro);
--- ---
<article itemscope itemtype="http://schema.org/BlogPosting" class="article"> <article itemscope itemtype="http://schema.org/BlogPosting" class="article">
<header> <header>
<div class="tags"> <div>
<ul itemprop="keywords"> <div class="tags">
{ <ul itemprop="keywords">
tags.map((tag) => ( {
<li> tags.map((tag) => (
<TranslatedLink rel="tag" to={`/articles/?tags=${tag.toLocaleLowerCase()}`}>{`${tag}${ <li>
tags.at(-1) === tag ? "" : "," <TranslatedLink rel="tag" to={`/articles/?tags=${tag.toLocaleLowerCase()}`}>{`${tag}${
}`}</TranslatedLink> tags.at(-1) === tag ? "" : ","
</li> }`}</TranslatedLink>
)) </li>
} ))
</ul> }
</ul>
</div>
<div class="views">
<Icon name="eye" width="21" height="21" />
<span><span id="views">---</span>&nbsp;{t("article.views")}</span>
</div>
</div> </div>
<div class="heading"> <div class="heading">
<h1 itemprop="headline">{title}</h1> <h1 itemprop="headline">{title}</h1>
@ -65,3 +74,13 @@ const { title, description, coverImage, tags, author, published_at } = data;
<slot /> <slot />
<ArticleComments /> <ArticleComments />
</article> </article>
<script define:vars={{ slug }}>
(async () => {
const response = await fetch(`/api/views?slug=${slug}`, { method: "POST" });
const views = await response.text();
const $views = document.getElementById("views");
if ($views) $views.textContent = views;
})();
</script>

View File

@ -1,5 +1,18 @@
.article { .article {
& header { & header {
& div:nth-child(1) {
display: flex;
justify-content: space-between;
align-items: center;
& .views {
display: flex;
align-items: center;
& span {
margin-left: 3.5px;
font-size: var(--font-size-sm);
}
}
}
& .heading { & .heading {
margin-top: 1.125rem; margin-top: 1.125rem;
@ -62,8 +75,15 @@
@media screen and (max-width: 560px) { @media screen and (max-width: 560px) {
.article { .article {
& header .heading h1 { & header {
font-size: 2rem; & div:nth-child(1) {
flex-direction: column-reverse;
row-gap: 10px;
align-items: flex-start;
}
& .heading h1 {
font-size: 2rem;
}
} }
} }
} }

View File

@ -6,6 +6,7 @@ export default {
"article.availableOn": "Available on", "article.availableOn": "Available on",
"articles.empty": "No articles found...", "articles.empty": "No articles found...",
"articlePost.comments": "Comments", "articlePost.comments": "Comments",
"article.views": "views",
"recentArticles.empty": "There is nothing here yet...", "recentArticles.empty": "There is nothing here yet...",
/* Page titles */ /* Page titles */
"pageTitle.about": "About me", "pageTitle.about": "About me",

View File

@ -6,6 +6,7 @@ export default {
"article.availableOn": "Доступно на", "article.availableOn": "Доступно на",
"articles.empty": "Не найдено ни одной статьи...", "articles.empty": "Не найдено ни одной статьи...",
"articlePost.comments": "Комментарии", "articlePost.comments": "Комментарии",
"article.views": "просмотров",
"recentArticles.empty": "Пока что здесь ничего нет...", "recentArticles.empty": "Пока что здесь ничего нет...",
/* Page titles */ /* Page titles */
"pageTitle.about": "Обо мне", "pageTitle.about": "Обо мне",

3
src/icons/eye.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.45443 11.6532C3.37609 11.8671 3.21746 12.042 3.01222 12.1408C2.80699 12.2395 2.57133 12.2544 2.35533 12.1821C2.13932 12.1099 1.95999 11.9563 1.85545 11.754C1.7509 11.5516 1.72937 11.3165 1.79543 11.0985C1.77793 11.151 1.79543 11.0968 1.79543 11.0968C1.82655 11.0004 1.86277 10.9058 1.90393 10.8132C1.97393 10.647 2.07718 10.4177 2.21893 10.1465C2.50768 9.604 2.95743 8.88125 3.62068 8.1585C4.95943 6.69725 7.15218 5.25 10.4999 5.25C13.8477 5.25 16.0404 6.69725 17.3792 8.1585C18.0938 8.94246 18.6733 9.83956 19.0942 10.8132L19.1747 11.011C19.1799 11.025 19.1974 11.116 19.2149 11.2035L19.2499 11.375C19.2499 11.375 19.3969 11.9577 18.6514 12.2045C18.4319 12.2778 18.1922 12.2612 17.9849 12.1582C17.7777 12.0552 17.6196 11.8743 17.5454 11.655V11.6498L17.5349 11.6217C17.447 11.3969 17.3465 11.1773 17.2339 10.9637C16.9228 10.3767 16.5382 9.83153 16.0894 9.3415C15.0219 8.17775 13.2772 7 10.4999 7C7.72268 7 5.97793 8.17775 4.91043 9.3415C4.33248 9.97512 3.86247 10.6993 3.51918 11.4853C3.50082 11.5304 3.48332 11.5759 3.46668 11.6217L3.45443 11.6532V11.6532ZM6.99993 12.25C6.99993 11.3217 7.36868 10.4315 8.02506 9.77513C8.68143 9.11875 9.57167 8.75 10.4999 8.75C11.4282 8.75 12.3184 9.11875 12.9748 9.77513C13.6312 10.4315 13.9999 11.3217 13.9999 12.25C13.9999 13.1783 13.6312 14.0685 12.9748 14.7249C12.3184 15.3813 11.4282 15.75 10.4999 15.75C9.57167 15.75 8.68143 15.3813 8.02506 14.7249C7.36868 14.0685 6.99993 13.1783 6.99993 12.25V12.25Z" fill="#21262D"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -3,6 +3,7 @@
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"moduleResolution": "node", "moduleResolution": "node",
"esModuleInterop": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"types": ["vite/client", "@astrojs/image/client", "@types/mdx"], "types": ["vite/client", "@astrojs/image/client", "@types/mdx"],