diff --git a/.gitignore b/.gitignore
index ebc9a5a..2b8d69b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,5 +35,7 @@ pnpm-debug.log*
!.yarn/versions
.pnp.*
-# blog
-src/content/blog
+# blog, meta
+src/content/blog/*
+Matomo.astro
+src/pages/pages/*
diff --git a/README.md b/README.md
index 9f35789..f650606 100644
--- a/README.md
+++ b/README.md
@@ -20,13 +20,14 @@ The website will be built to the `./dist` folder.
## Blogging with Joplin
-You can use Joplin to write and manageyour blog posts, instead of just placing the Markdown files in the
+You can use Joplin to write and manage your blog posts, instead of just placing the Markdown files in the
`src/content/blog` folder.
1. Install [Joplin](https://joplinapp.org/)
2. Start the Web Clipper Service: Tools > Options > Web Clipper > Start Web Clipper Service
3. Run `npm run joplin`
4. Allow the request for an API token on the Joplin app
+ - You can also place the Joplin token in an `.env` file in the format `JOPLIN_KEY=your_token`.
The script will look for a notebook named `Blog`. It will then download all notes from that notebook, alongside their
attachments, and place them in the `src/content/blog` folder.
@@ -46,6 +47,22 @@ In order, to have an ogImage (also known as article cover):
```
Note the `.` (dot) instead of `:` (colon)!
+## Uploading to WebDav
+
+I have found that a quick and easy way to deploy the website is via WebDav and compatible software on the server side, like
+[this](https://github.com/mholt/caddy-webdav) plugin for Caddy.
+
+To simplify uploading to WebDav, I have created a script, which can be run with `npm run upload`.
+
+You have to set the following environment variables:
+- `WEBDAV_ENDPOINT`: The URL of the WebDav server, e.g. `https://example.com/webdav/`
+- `WEBDAV_USERNAME`: The username for the WebDav server
+- `WEBDAV_PASSWORD`: The password for the WebDav server
+- `WEBDAV_PATH`: The path to the folder on the server, e.g. `/public_html/` (default: `/`)
+
+The script will upload the `./dist` folder, so make sure you run `npm run build` before the upload script.
+Note that running the script will clear the remote folder before uploading the files.
+
## License
The original [Astro Paper](https://github.com/satnaing/astro-paper) is licensed under the MIT License, Copyright © 2025
diff --git a/package-lock.json b/package-lock.json
index 37dd0cd..542f094 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,7 +25,8 @@
"sass": "^1.77.2",
"satori": "^0.10.11",
"tailwindcss": "^3.4.1",
- "typescript": "^5.4.5"
+ "typescript": "^5.4.5",
+ "webdav": "^5.8.0"
},
"devDependencies": {
"@astrojs/react": "^3.0.9",
@@ -40,6 +41,7 @@
"astro-eslint-parser": "^0.16.2",
"commitizen": "^4.3.0",
"cz-conventional-changelog": "^3.3.0",
+ "dotenv": "^16.4.7",
"eslint": "^8.56.0",
"eslint-plugin-astro": "^0.31.3",
"husky": "^8.0.3",
@@ -739,6 +741,15 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@buttercup/fetch": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@buttercup/fetch/-/fetch-0.2.1.tgz",
+ "integrity": "sha512-sCgECOx8wiqY8NN1xN22BqqKzXYIG2AicNLlakOAI4f0WgyLVUbAigMf8CZhBtJxdudTcB1gD5lciqi44jwJvg==",
+ "license": "MIT",
+ "optionalDependencies": {
+ "node-fetch": "^3.3.0"
+ }
+ },
"node_modules/@commitlint/config-validator": {
"version": "17.6.7",
"resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.6.7.tgz",
@@ -4236,6 +4247,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/byte-length/-/byte-length-1.0.2.tgz",
+ "integrity": "sha512-ovBpjmsgd/teRmgcPh23d4gJvxDoXtAzEL9xTfMU8Yc2kqCDb7L9jAG0XHl1nzuGl+h3ebCIF1i62UFyA9V/2Q==",
+ "license": "MIT"
+ },
"node_modules/cachedir": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz",
@@ -4365,6 +4382,15 @@
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true
},
+ "node_modules/charenc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+ "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/cheerio": {
"version": "1.0.0-rc.12",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
@@ -4987,6 +5013,15 @@
"node": ">= 8"
}
},
+ "node_modules/crypt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+ "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/css-background-parser": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/css-background-parser/-/css-background-parser-0.1.0.tgz",
@@ -5120,6 +5155,15 @@
"@commitlint/load": ">6.1.1"
}
},
+ "node_modules/data-uri-to-buffer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -5470,6 +5514,19 @@
"tslib": "^2.0.3"
}
},
+ "node_modules/dotenv": {
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
"node_modules/dset": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/dset/-/dset-3.1.3.tgz",
@@ -6154,21 +6211,18 @@
"dev": true
},
"node_modules/fast-xml-parser": {
- "version": "4.2.7",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.7.tgz",
- "integrity": "sha512-J8r6BriSLO1uj2miOk1NW0YVm8AGOOu3Si2HQp/cSmo6EA4m3fcwu2WKjJ4RK9wMLBtg69y1kS8baDiQBR41Ig==",
+ "version": "4.5.3",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz",
+ "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==",
"funding": [
- {
- "type": "paypal",
- "url": "https://paypal.me/naturalintelligence"
- },
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
+ "license": "MIT",
"dependencies": {
- "strnum": "^1.0.5"
+ "strnum": "^1.1.1"
},
"bin": {
"fxparser": "src/cli/cli.js"
@@ -6190,6 +6244,29 @@
"pend": "~1.2.0"
}
},
+ "node_modules/fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
+ },
+ "engines": {
+ "node": "^12.20 || >= 14.13"
+ }
+ },
"node_modules/fflate": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz",
@@ -6333,6 +6410,18 @@
"node": ">=8"
}
},
+ "node_modules/formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "license": "MIT",
+ "dependencies": {
+ "fetch-blob": "^3.1.2"
+ },
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
"node_modules/fraction.js": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz",
@@ -6852,6 +6941,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/hot-patcher": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/hot-patcher/-/hot-patcher-2.0.1.tgz",
+ "integrity": "sha512-ECg1JFG0YzehicQaogenlcs2qg6WsXQsxtnbr1i696u5tLUjtJdQAh0u2g0Q5YV45f263Ta1GnUJsc8WIfJf4Q==",
+ "license": "MIT"
+ },
"node_modules/html-escaper": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
@@ -7654,6 +7749,12 @@
"resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz",
"integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="
},
+ "node_modules/layerr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/layerr/-/layerr-3.0.0.tgz",
+ "integrity": "sha512-tv754Ki2dXpPVApOrjTyRo4/QegVb9eVFq4mjqp4+NM5NaX7syQvN5BBNfV/ZpAHCEHV24XdUVrBAoka4jt3pA==",
+ "license": "MIT"
+ },
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -8433,6 +8534,23 @@
"node": ">= 18"
}
},
+ "node_modules/md5": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
+ "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "charenc": "0.0.2",
+ "crypt": "0.0.2",
+ "is-buffer": "~1.1.6"
+ }
+ },
+ "node_modules/md5/node_modules/is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "license": "MIT"
+ },
"node_modules/mdast-util-definitions": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz",
@@ -9505,6 +9623,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/nested-property": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/nested-property/-/nested-property-4.0.0.tgz",
+ "integrity": "sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA==",
+ "license": "MIT"
+ },
"node_modules/nlcst-to-string": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-3.1.1.tgz",
@@ -9545,6 +9669,43 @@
"integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==",
"optional": true
},
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "license": "MIT",
+ "dependencies": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
+ }
+ },
"node_modules/node-releases": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
@@ -10050,6 +10211,12 @@
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
+ "node_modules/path-posix": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/path-posix/-/path-posix-1.0.0.tgz",
+ "integrity": "sha512-1gJ0WpNIiYcQydgg3Ed8KzvIqTsDpNwq+cjBCssvBtuTWjEqY1AW+i+OepiEMqDCzyro9B2sLAe4RBPajMYFiA==",
+ "license": "ISC"
+ },
"node_modules/path-to-regexp": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz",
@@ -10690,6 +10857,12 @@
"node": ">=6"
}
},
+ "node_modules/querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "license": "MIT"
+ },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -11033,6 +11206,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "license": "MIT"
+ },
"node_modules/resolve": {
"version": "1.22.4",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",
@@ -12265,9 +12444,16 @@
}
},
"node_modules/strnum": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
- "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz",
+ "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
},
"node_modules/strtok3": {
"version": "7.0.0",
@@ -13175,6 +13361,25 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/url-join": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz",
+ "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "license": "MIT",
+ "dependencies": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -13897,6 +14102,76 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/web-streams-polyfill": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/webdav": {
+ "version": "5.8.0",
+ "resolved": "https://registry.npmjs.org/webdav/-/webdav-5.8.0.tgz",
+ "integrity": "sha512-iuFG7NamJ41Oshg4930iQgfIpRrUiatPWIekeznYgEf2EOraTRcDPTjy7gIOMtkdpKTaqPk1E68NO5PAGtJahA==",
+ "license": "MIT",
+ "dependencies": {
+ "@buttercup/fetch": "^0.2.1",
+ "base-64": "^1.0.0",
+ "byte-length": "^1.0.2",
+ "entities": "^6.0.0",
+ "fast-xml-parser": "^4.5.1",
+ "hot-patcher": "^2.0.1",
+ "layerr": "^3.0.0",
+ "md5": "^2.3.0",
+ "minimatch": "^9.0.5",
+ "nested-property": "^4.0.0",
+ "node-fetch": "^3.3.2",
+ "path-posix": "^1.0.0",
+ "url-join": "^5.0.0",
+ "url-parse": "^1.5.10"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/webdav/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/webdav/node_modules/entities": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz",
+ "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/webdav/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/whatwg-encoding": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
diff --git a/package.json b/package.json
index 67286b0..101f668 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,8 @@
"cz": "cz",
"prepare": "husky install",
"lint": "eslint .",
- "joplin": "node src/utils/joplin.js"
+ "joplin": "node src/utils/joplin.mjs",
+ "upload": "node src/utils/upload.mjs"
},
"dependencies": {
"@astrojs/check": "^0.7.0",
@@ -33,7 +34,8 @@
"sass": "^1.77.2",
"satori": "^0.10.11",
"tailwindcss": "^3.4.1",
- "typescript": "^5.4.5"
+ "typescript": "^5.4.5",
+ "webdav": "^5.8.0"
},
"devDependencies": {
"@astrojs/react": "^3.0.9",
@@ -48,6 +50,7 @@
"astro-eslint-parser": "^0.16.2",
"commitizen": "^4.3.0",
"cz-conventional-changelog": "^3.3.0",
+ "dotenv": "^16.4.7",
"eslint": "^8.56.0",
"eslint-plugin-astro": "^0.31.3",
"husky": "^8.0.3",
diff --git a/src/components/Matomo.astro b/src/components/Matomo.astro
index a8f7e7c..8324dee 100644
--- a/src/components/Matomo.astro
+++ b/src/components/Matomo.astro
@@ -1,12 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/utils/joplin.js b/src/utils/joplin.mjs
similarity index 94%
rename from src/utils/joplin.js
rename to src/utils/joplin.mjs
index 98d67a3..d157435 100644
--- a/src/utils/joplin.js
+++ b/src/utils/joplin.mjs
@@ -1,6 +1,12 @@
-const http = require('node:http');
-const fs = require('fs');
-const path = require('path');
+import dotenv from 'dotenv';
+dotenv.config();
+
+import http from 'node:http';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
async function fetch(url, method = "GET") {
return new Promise((resolve) => {
@@ -172,16 +178,18 @@ async function clearBlogFolder() {
}
async function main() {
-
console.log("Finding JoplinClipperServer port...");
let host = "http://localhost:" + await findPort();
- let apiToken = await authenticate(host);
- //let apiToken = "";
+
+ let apiToken = process.env.JOPLIN_KEY;
+ if (!apiToken) {
+ apiToken = await authenticate(host);
+ }
+
let notes = await getNotebookNotes(apiToken, host);
await clearBlogFolder();
for(let noteId of notes) {
-
let noteBody = await getNoteBody(apiToken, host, noteId);
let slug = await getNoteSlug(noteBody);
diff --git a/src/utils/upload.mjs b/src/utils/upload.mjs
new file mode 100644
index 0000000..76bec24
--- /dev/null
+++ b/src/utils/upload.mjs
@@ -0,0 +1,96 @@
+import dotenv from 'dotenv';
+dotenv.config();
+
+import { createClient } from 'webdav';
+import fs from 'fs';
+import path from 'path';
+
+const client = createClient(
+ process.env.WEBDAV_ENDPOINT,
+ {
+ username: process.env.WEBDAV_USERNAME,
+ password: process.env.WEBDAV_PASSWORD,
+ maxBodyLength: Infinity,
+ maxContentLength: Infinity,
+ headers: {
+ Accept: "*/*",
+ Connection: "keep-alive"
+ }
+ }
+);
+
+async function createDirectoryIfNotExist(remotePath) {
+ try {
+ const exists = await client.exists(remotePath);
+ if (!exists) {
+ await client.createDirectory(remotePath);
+ }
+ } catch (error) {
+ if (error.status !== 409 && error.status !== 405) {
+ throw error;
+ }
+ }
+}
+
+async function uploadDirectory(localPath, remoteBasePath) {
+ try {
+ const items = fs.readdirSync(localPath, { withFileTypes: true });
+
+ for (const item of items) {
+ const localItemPath = path.join(localPath, item.name);
+ const remoteItemPath = path.posix.join(remoteBasePath, item.name);
+
+ if (item.isDirectory()) {
+ // Create directory on WebDAV server
+ await createDirectoryIfNotExist(remoteItemPath);
+ console.log(`Verified directory: ${remoteItemPath}`);
+ await uploadDirectory(localItemPath, remoteItemPath);
+ } else {
+ // Upload file
+ console.log(`Uploading file: ${remoteItemPath}`);
+ const content = fs.readFileSync(localItemPath);
+ await client.putFileContents(remoteItemPath, content);
+ }
+ }
+ } catch (error) {
+ console.error(`Error processing ${localPath}:`, error.message);
+ throw error;
+ }
+}
+
+async function clearFolder(remotePath) {
+ try {
+ const items = await client.getDirectoryContents(remotePath);
+ for (const item of items) {
+ if (item.basename !== '..') {
+ const itemPath = path.posix.join(remotePath, item.basename);
+ await client.deleteFile(itemPath);
+ }
+ }
+ } catch (error) {
+ console.error('Error clearing root folder:', error.message);
+ throw error;
+ }
+}
+
+// Usage example
+async function main() {
+ const localDir = './dist';
+ let remoteDir = process.env.WEBDAV_REMOTE_DIR;
+ if(remoteDir === undefined) {
+ remoteDir = '/';
+ }
+
+ try {
+ // Clear root folder
+ await clearFolder(remoteDir);
+
+ // Upload new content
+ await uploadDirectory(localDir, remoteDir);
+ console.log('Directory upload complete');
+ } catch (error) {
+ console.error('Upload failed:', error.message);
+ }
+}
+
+main();