├── backend ├── data │ ├── nextThemeIndex.json │ └── thmesForStories.json ├── index.js ├── cacher │ ├── cacherSingleton.js │ └── cacher.js ├── .env.sample ├── db │ ├── pool.js │ ├── create.js │ └── db.sql ├── mimeTypes │ └── mimetypes.js ├── .gitignore ├── gpt-api │ ├── toolsesForApi.js │ └── gptApi.js ├── package.json ├── fs │ └── fs.js ├── .eslintrc.js ├── controllers │ ├── ws.js │ ├── static.js │ ├── login.js │ ├── feedback.js │ ├── story.js │ └── comment.js ├── config │ └── config.js ├── logger │ └── logger.js ├── server.js └── contentGenerator.js ├── adminPanel ├── .env.sample ├── src │ ├── vite-env.d.ts │ ├── main.js │ ├── utils.js │ ├── components │ │ ├── Feedbacks.svelte │ │ ├── Stories.svelte │ │ ├── Comments.svelte │ │ ├── Feedback.svelte │ │ ├── Login.svelte │ │ ├── Comment.svelte │ │ ├── Story.svelte │ │ └── Navigation.svelte │ ├── app.css │ ├── App.svelte │ ├── store.js │ └── assets │ │ └── svelte.svg ├── vite.config.js ├── svelte.config.js ├── package.json ├── .gitignore ├── index.html ├── jsconfig.json ├── README.md └── package-lock.json ├── jungleBlog ├── .env.sample ├── src │ ├── vite-env.d.ts │ ├── articlesStore.js │ ├── main.js │ ├── websocketStore.js │ ├── components │ │ ├── Footer.svelte │ │ ├── Card.svelte │ │ ├── Main.svelte │ │ ├── AboutUs.svelte │ │ ├── Feedback.svelte │ │ ├── Content.svelte │ │ ├── Header.svelte │ │ └── Article.svelte │ ├── App.svelte │ ├── assets │ │ └── svelte.svg │ ├── app.css │ └── bannedWords.js ├── public │ └── res │ │ ├── fav.ico │ │ ├── cursor.png │ │ ├── AboutUs.png │ │ ├── mainPhoto.png │ │ ├── pointer.png │ │ └── mainPhotoDark.png ├── svelte.config.js ├── .gitignore ├── vite.config.js ├── package.json ├── index.html ├── jsconfig.json ├── README.md └── package-lock.json ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── LICENSE ├── CONTRIBUTING.md ├── README.md └── CODE_OF_CONDUCT.md /backend/data/nextThemeIndex.json: -------------------------------------------------------------------------------- 1 | {"themeIndex":0} -------------------------------------------------------------------------------- /adminPanel/.env.sample: -------------------------------------------------------------------------------- 1 | VITE_HOST= 2 | VITE_PROTOCOL= 3 | VITE_WS_PROTOCOL= -------------------------------------------------------------------------------- /jungleBlog/.env.sample: -------------------------------------------------------------------------------- 1 | VITE_SERVER_URL= 2 | VITE_PROTOCOL= 3 | VITE_WS_PROTOCOL= -------------------------------------------------------------------------------- /adminPanel/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /jungleBlog/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /jungleBlog/public/res/fav.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebladehit/jungleBlog/HEAD/jungleBlog/public/res/fav.ico -------------------------------------------------------------------------------- /jungleBlog/public/res/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebladehit/jungleBlog/HEAD/jungleBlog/public/res/cursor.png -------------------------------------------------------------------------------- /jungleBlog/public/res/AboutUs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebladehit/jungleBlog/HEAD/jungleBlog/public/res/AboutUs.png -------------------------------------------------------------------------------- /jungleBlog/public/res/mainPhoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebladehit/jungleBlog/HEAD/jungleBlog/public/res/mainPhoto.png -------------------------------------------------------------------------------- /jungleBlog/public/res/pointer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebladehit/jungleBlog/HEAD/jungleBlog/public/res/pointer.png -------------------------------------------------------------------------------- /jungleBlog/public/res/mainPhotoDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebladehit/jungleBlog/HEAD/jungleBlog/public/res/mainPhotoDark.png -------------------------------------------------------------------------------- /backend/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('./server.js'); 4 | const { startGenerateContent } = require('./contentGenerator.js'); 5 | 6 | startGenerateContent(); -------------------------------------------------------------------------------- /adminPanel/src/main.js: -------------------------------------------------------------------------------- 1 | import './app.css' 2 | import App from './App.svelte' 3 | 4 | const app = new App({ 5 | target: document.getElementById('app'), 6 | }) 7 | 8 | export default app 9 | -------------------------------------------------------------------------------- /adminPanel/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import { svelte } from '@sveltejs/vite-plugin-svelte' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [svelte()], 7 | }) 8 | -------------------------------------------------------------------------------- /backend/cacher/cacherSingleton.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { Cacher } = require('./cacher.js'); 4 | const { CACHE_LIMIT_ITEMS } = require('../config/config.js'); 5 | 6 | const cacher = new Cacher(CACHE_LIMIT_ITEMS); 7 | 8 | module.exports = { cacher }; -------------------------------------------------------------------------------- /adminPanel/svelte.config.js: -------------------------------------------------------------------------------- 1 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' 2 | 3 | export default { 4 | // Consult https://svelte.dev/docs#compile-time-svelte-preprocess 5 | // for more information about preprocessors 6 | preprocess: vitePreprocess(), 7 | } 8 | -------------------------------------------------------------------------------- /jungleBlog/svelte.config.js: -------------------------------------------------------------------------------- 1 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' 2 | 3 | export default { 4 | // Consult https://svelte.dev/docs#compile-time-svelte-preprocess 5 | // for more information about preprocessors 6 | preprocess: vitePreprocess(), 7 | } 8 | -------------------------------------------------------------------------------- /backend/.env.sample: -------------------------------------------------------------------------------- 1 | PORT= 2 | HOST= 3 | PROTOCOL= 4 | DB_PORT= 5 | DB_NAME= 6 | DB_USER= 7 | DB_PASSWORD= 8 | OPENAI_TOKEN= 9 | ADMIN_PASS= 10 | ADMIN_LOGIN= 11 | GPT_MODEL_TEXT= 12 | GPT_MODEL_IMAGE= 13 | TIME_ZONE= 14 | POSTING_STORY_TIME= 15 | POSTING_INTERVAL= 16 | IMAGE_QUALITY= 17 | CACHE_LIMIT_ITEMS= 18 | RENEW_TOKEN_TIME= -------------------------------------------------------------------------------- /backend/db/pool.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { Pool } = require('pg'); 4 | const { HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD } = require('../config/config.js'); 5 | 6 | const pool = new Pool({ 7 | host: HOST, 8 | port: DB_PORT, 9 | database: DB_NAME, 10 | user: DB_USER, 11 | password: DB_PASSWORD 12 | }); 13 | 14 | module.exports = { pool }; -------------------------------------------------------------------------------- /backend/mimeTypes/mimetypes.js: -------------------------------------------------------------------------------- 1 | const MIME_TYPES = { 2 | html: 'text/html; charset=UTF-8', 3 | js: 'application/javascript; charset=UTF-8', 4 | json: 'application/json', 5 | css: 'text/css', 6 | png: 'image/png', 7 | jpg: 'image/jpg', 8 | ico: 'image/x-icon', 9 | svg: 'image/svg+xml', 10 | txt: 'text/plain', 11 | }; 12 | 13 | module.exports = { MIME_TYPES }; -------------------------------------------------------------------------------- /adminPanel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adminpanel", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@sveltejs/vite-plugin-svelte": "^3.0.1", 13 | "svelte": "^4.2.8", 14 | "vite": "^5.0.8" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /adminPanel/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | .env 15 | 16 | # Editor directories and files 17 | .vscode 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | .env 15 | 16 | # Editor directories and files 17 | .vscode 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | -------------------------------------------------------------------------------- /jungleBlog/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | .env 15 | 16 | # Editor directories and files 17 | .vscode 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | -------------------------------------------------------------------------------- /jungleBlog/src/articlesStore.js: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | export const articlesData = writable([]); 4 | 5 | export async function fetchArticles() { 6 | const url = '/story'; 7 | const response = await fetch(url); 8 | if (!response.ok) { 9 | throw new Error(`HTTP error! ${response.status}`); 10 | } 11 | const data = await response.json(); 12 | articlesData.set(data); 13 | } -------------------------------------------------------------------------------- /adminPanel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Admin Panel 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /jungleBlog/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import { svelte } from '@sveltejs/vite-plugin-svelte' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [svelte()], 7 | server: { 8 | proxy: { 9 | '/api': { 10 | target: 'http://localhost:3000', 11 | changeOrigin: true, 12 | rewrite: (path) => path.replace(/^\/api/, '') 13 | } 14 | } 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /backend/gpt-api/toolsesForApi.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const requestBenchMark = async (fn, ...params) => { 4 | const startTime = Date.now(); 5 | const data = await fn(...params); 6 | const deltaTime = Date.now() - startTime; 7 | return {data, deltaTime}; 8 | }; 9 | 10 | const convertFromB64toBuffer = (stringB64) => { 11 | return Buffer.from(stringB64, 'base64'); 12 | }; 13 | 14 | module.exports = { 15 | requestBenchMark, 16 | convertFromB64toBuffer 17 | }; -------------------------------------------------------------------------------- /jungleBlog/src/main.js: -------------------------------------------------------------------------------- 1 | import './app.css'; 2 | import App from './App.svelte'; 3 | import { initWebSocket } from './websocketStore.js'; 4 | 5 | const url = import.meta.env.VITE_SERVER_URL; 6 | initWebSocket(url); 7 | 8 | document.addEventListener('DOMContentLoaded', () => { 9 | document.body.dataset.theme = localStorage.getItem('theme') || 'default'; 10 | }); 11 | 12 | const app = new App({ 13 | target: document.getElementById('app'), 14 | }); 15 | 16 | export default app; -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "dotenv": "^16.3.1", 15 | "openai": "^4.22.0", 16 | "pg": "^8.11.3", 17 | "ws": "^8.16.0" 18 | }, 19 | "devDependencies": { 20 | "eslint": "^8.56.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /adminPanel/src/utils.js: -------------------------------------------------------------------------------- 1 | export const formatDateTime = (inputDate) => { 2 | const date = new Date(inputDate); 3 | 4 | const day = String(date.getDate()).padStart(2, '0'); 5 | const month = String(date.getMonth() + 1).padStart(2, '0'); 6 | const year = String(date.getFullYear()).slice(-2); 7 | const hours = String(date.getHours()).padStart(2, '0'); 8 | const minutes = String(date.getMinutes()).padStart(2, '0'); 9 | const seconds = String(date.getSeconds()).padStart(2, '0'); 10 | 11 | return `${day}.${month}.${year} ${hours}:${minutes}:${seconds}`; 12 | }; -------------------------------------------------------------------------------- /jungleBlog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jungleblog", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview", 10 | "buildToBackLinux": "vite build && cp -r ./dist/* ../backend/static", 11 | "buildToBackWindows": "vite build && rmdir /s /q ..\\backend\\static && xcopy /s /e /h dist ..\\backend\\static" 12 | }, 13 | "devDependencies": { 14 | "@sveltejs/vite-plugin-svelte": "^2.4.2", 15 | "svelte": "^4.0.5", 16 | "vite": "^4.4.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /adminPanel/src/components/Feedbacks.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 |
10 | feedbacks 11 |
12 | 13 | {#if $feedbacksData} 14 |
15 | {#each $feedbacksData as item (item.feedback_id)} 16 | 17 | {/each} 18 |
19 | {:else} 20 |

Loading...

21 | {/if} 22 |
23 | 24 | -------------------------------------------------------------------------------- /jungleBlog/src/websocketStore.js: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | export const websocket = writable(null); 4 | 5 | export function initWebSocket(url) { 6 | let ws = new WebSocket(`${import.meta.env.VITE_WS_PROTOCOL}://${url}`); 7 | 8 | ws.onopen = () => { 9 | console.log('WebSocket connected'); 10 | }; 11 | 12 | ws.onclose = () => { 13 | console.log('WebSocket disconnected, trying to reconnect...'); 14 | setTimeout(() => initWebSocket(url), 3000); 15 | }; 16 | 17 | ws.onerror = (error) => { 18 | console.error('WebSocket error:', error); 19 | ws.close(); 20 | }; 21 | 22 | websocket.set(ws); 23 | } -------------------------------------------------------------------------------- /backend/db/create.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('node:fs'); 4 | const { Client } = require('pg'); 5 | require('dotenv').config({ path: '../.env' }); 6 | const { HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD } = require('../config/config.js'); 7 | 8 | const script = fs.readFileSync('./db.sql', 'utf-8'); 9 | 10 | (async () => { 11 | const client = new Client({ 12 | host: HOST, 13 | port: DB_PORT, 14 | database: DB_NAME, 15 | user: DB_USER, 16 | password: DB_PASSWORD 17 | }); 18 | 19 | await client.connect(); 20 | const result = await client.query(script); 21 | console.log(result); 22 | await client.end(); 23 | })(); -------------------------------------------------------------------------------- /jungleBlog/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jungle Blog 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /adminPanel/src/components/Stories.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 |
14 | stories 15 |
16 | {#if storiesData} 17 |
18 | {#each $storiesData as item (item.story_id)} 19 | 20 | {/each} 21 |
22 | {:else} 23 |

Loading...

24 | {/if} 25 |
26 | 27 | -------------------------------------------------------------------------------- /backend/gpt-api/gptApi.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const generateText = async (openai, model, messages, maxTokens = 2000) => { 4 | const response = await openai.chat.completions.create({ 5 | model, 6 | messages, 7 | temperature: 1, 8 | max_tokens: maxTokens, 9 | top_p: 1, 10 | frequency_penalty: 0, 11 | presence_penalty: 0 12 | }); 13 | return response.choices[0].message.content; 14 | }; 15 | 16 | const genetateImage = async (openai, model, prompt, quantity, size, response_format = 'b64_json') => { 17 | const response = await openai.images.generate({ 18 | model, 19 | prompt, 20 | n: quantity, 21 | size, 22 | response_format 23 | }); 24 | return response.data[0]; 25 | }; 26 | 27 | module.exports = { 28 | generateText, genetateImage 29 | }; -------------------------------------------------------------------------------- /adminPanel/src/components/Comments.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 |
14 | comments 15 |
16 | 17 | {#if $commentsData} 18 |
19 | {#each $commentsData as item (item.comment_id)} 20 | 21 | {/each} 22 |
23 | {:else} 24 |

Loading...

25 | {/if} 26 | 27 |
28 | 29 | -------------------------------------------------------------------------------- /backend/fs/fs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fsp = require('node:fs').promises; 4 | 5 | const readFile = async (path) => { 6 | const data = await fsp.readFile(path); 7 | return data; 8 | }; 9 | 10 | const isFileExist = async (filePath) => { 11 | try { 12 | await fsp.access(filePath, fsp.constants.R_OK); 13 | return true; 14 | } catch (err) { 15 | return false; 16 | } 17 | }; 18 | 19 | const appendFile = async (path, data) => { 20 | await fsp.appendFile(path, data); 21 | }; 22 | 23 | const writeFile = async (path, data) => { 24 | await fsp.writeFile(path, data); 25 | }; 26 | 27 | const createDir = async (dirPath) => { 28 | if (!await isFileExist(dirPath)) { 29 | await fsp.mkdir(dirPath); 30 | } 31 | }; 32 | 33 | module.exports = { readFile, isFileExist, writeFile, createDir, appendFile }; -------------------------------------------------------------------------------- /jungleBlog/src/components/Footer.svelte: -------------------------------------------------------------------------------- 1 |
2 | © 2024 Kobbasa and TheBladeHit. All rights reserved. 3 |
4 | 5 | 32 | -------------------------------------------------------------------------------- /backend/db/db.sql: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA IF NOT EXISTS jungleBlog; 2 | 3 | SET search_path TO jungleBlog, public; 4 | 5 | CREATE TABLE IF NOT EXISTS jungleBlog.stories ( 6 | story_id SERIAL PRIMARY KEY, 7 | title VARCHAR(255) NOT NULL, 8 | content TEXT, 9 | image_url VARCHAR(255), 10 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 11 | ); 12 | 13 | CREATE TABLE IF NOT EXISTS jungleBlog.comments ( 14 | comment_id SERIAL PRIMARY KEY, 15 | story_id INTEGER REFERENCES jungleBlog.stories(story_id), 16 | username VARCHAR(50) NOT NULL, 17 | comment_text TEXT, 18 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 19 | ); 20 | 21 | CREATE TABLE IF NOT EXISTS jungleBlog.feedbacks ( 22 | feedback_id SERIAL PRIMARY KEY, 23 | name VARCHAR(50) NOT NULL, 24 | text VARCHAR(255) NOT NULL, 25 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 26 | ); -------------------------------------------------------------------------------- /backend/cacher/cacher.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class Cacher { 4 | cache = new Map(); 5 | 6 | constructor(maxItemInCache) { 7 | this.maxItemInCache = maxItemInCache; 8 | } 9 | 10 | setCache(key, value) { 11 | if (this.cache.size > this.maxItemInCache) this.releaseCache(); 12 | value.usage = 0; 13 | this.cache.set(key, value); 14 | } 15 | 16 | getCache(key) { 17 | const cache = this.cache.get(key); 18 | if (cache) cache.usage++; 19 | return cache; 20 | } 21 | 22 | deleteCache(key) { 23 | this.cache.delete(key); 24 | } 25 | 26 | releaseCache() { 27 | let minKey; 28 | let minValue = Infinity; 29 | for (const [key, value] of this.cache) { 30 | if (value.usage < minValue) { 31 | minValue = value.usage; 32 | minKey = key; 33 | } 34 | } 35 | this.deleteCache(minKey); 36 | } 37 | } 38 | 39 | module.exports = { Cacher }; -------------------------------------------------------------------------------- /backend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'env': { 3 | 'browser': true, 4 | 'commonjs': true, 5 | 'es2021': true 6 | }, 7 | 'extends': 'eslint:recommended', 8 | 'overrides': [ 9 | { 10 | 'env': { 11 | 'node': true 12 | }, 13 | 'files': [ 14 | '.eslintrc.{js,cjs}' 15 | ], 16 | 'parserOptions': { 17 | 'sourceType': 'script' 18 | } 19 | } 20 | ], 21 | 'parserOptions': { 22 | 'ecmaVersion': 'latest' 23 | }, 24 | 'rules': { 25 | 'indent': [ 26 | 'error', 27 | 2 28 | ], 29 | 'linebreak-style': [ 30 | 'error', 31 | 'unix' 32 | ], 33 | 'quotes': [ 34 | 'error', 35 | 'single' 36 | ], 37 | 'semi': [ 38 | 'error', 39 | 'always' 40 | ] 41 | }, 42 | 'globals': { 43 | 'process': true, 44 | '__dirname': true, 45 | 'Buffer': true 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /adminPanel/src/components/Feedback.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | -------------------------------------------------------------------------------- /adminPanel/src/app.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color: rgba(255, 255, 255, 0.87); 7 | background-color: #242424; 8 | 9 | font-synthesis: none; 10 | text-rendering: optimizeLegibility; 11 | -webkit-font-smoothing: antialiased; 12 | -moz-osx-font-smoothing: grayscale; 13 | } 14 | 15 | body { 16 | margin: 0; 17 | min-width: 320px; 18 | min-height: 100vh; 19 | } 20 | 21 | button { 22 | border-radius: 8px; 23 | border: 1px solid transparent; 24 | padding: 0.6em 1.2em; 25 | font-size: 1em; 26 | font-weight: 500; 27 | font-family: inherit; 28 | background-color: #1a1a1a; 29 | color: #d9dafa; 30 | cursor: pointer; 31 | transition: 0.25s; 32 | } 33 | button:hover { 34 | border-color: #646cff; 35 | color: #646cff; 36 | } 37 | button:focus, 38 | button:focus-visible { 39 | outline: 4px auto -webkit-focus-ring-color; 40 | } -------------------------------------------------------------------------------- /adminPanel/src/App.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 | {#if logined} 25 | 26 | {:else} 27 | 28 | {/if} 29 |
-------------------------------------------------------------------------------- /jungleBlog/src/App.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | 13 |
14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 |
22 |
23 | 24 | 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /adminPanel/src/store.js: -------------------------------------------------------------------------------- 1 | import { writable } from "svelte/store"; 2 | 3 | export const commentsData = writable(); 4 | export const storiesData = writable(); 5 | export const feedbacksData = writable(); 6 | 7 | export const fetchComments = async () => { 8 | const response = await fetch(import.meta.env.VITE_PROTOCOL + '://' + import.meta.env.VITE_HOST + '/comment', { method: 'GET' }); 9 | if (!response.ok) throw new Error('Network error'); 10 | return await response.json(); 11 | }; 12 | 13 | export const fetchStories = async () => { 14 | const response = await fetch(import.meta.env.VITE_PROTOCOL + '://' + import.meta.env.VITE_HOST + '/story', { method: 'GET' }); 15 | if (!response.ok) throw new Error('Network error'); 16 | return await response.json(); 17 | }; 18 | 19 | export const fetchFeedbacks = async () => { 20 | const response = await fetch(import.meta.env.VITE_PROTOCOL + '://' + import.meta.env.VITE_HOST + '/feedbacks', { method: 'GET' }); 21 | if (!response.ok) throw new Error('Network error'); 22 | return await response.json(); 23 | }; -------------------------------------------------------------------------------- /adminPanel/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "bundler", 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | /** 7 | * svelte-preprocess cannot figure out whether you have 8 | * a value or a type, so tell TypeScript to enforce using 9 | * `import type` instead of `import` for Types. 10 | */ 11 | "verbatimModuleSyntax": true, 12 | "isolatedModules": true, 13 | "resolveJsonModule": true, 14 | /** 15 | * To have warnings / errors of the Svelte compiler at the 16 | * correct position, enable source maps by default. 17 | */ 18 | "sourceMap": true, 19 | "esModuleInterop": true, 20 | "skipLibCheck": true, 21 | /** 22 | * Typecheck JS in `.svelte` and `.js` files by default. 23 | * Disable this if you'd like to use dynamic types. 24 | */ 25 | "checkJs": true 26 | }, 27 | /** 28 | * Use global.d.ts instead of compilerOptions.types 29 | * to avoid limiting type declarations. 30 | */ 31 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] 32 | } 33 | -------------------------------------------------------------------------------- /jungleBlog/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "bundler", 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | /** 7 | * svelte-preprocess cannot figure out whether you have 8 | * a value or a type, so tell TypeScript to enforce using 9 | * `import type` instead of `import` for Types. 10 | */ 11 | "verbatimModuleSyntax": true, 12 | "isolatedModules": true, 13 | "resolveJsonModule": true, 14 | /** 15 | * To have warnings / errors of the Svelte compiler at the 16 | * correct position, enable source maps by default. 17 | */ 18 | "sourceMap": true, 19 | "esModuleInterop": true, 20 | "skipLibCheck": true, 21 | /** 22 | * Typecheck JS in `.svelte` and `.js` files by default. 23 | * Disable this if you'd like to use dynamic types. 24 | */ 25 | "checkJs": true 26 | }, 27 | /** 28 | * Use global.d.ts instead of compilerOptions.types 29 | * to avoid limiting type declarations. 30 | */ 31 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] 32 | } 33 | -------------------------------------------------------------------------------- /backend/controllers/ws.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const WebSocket = require('ws'); 4 | const { cacher } = require('../cacher/cacherSingleton.js'); 5 | 6 | const msgTypes = { 7 | newComment: (recieveData) => JSON.stringify({ msgType: 'reloadComments', data: { storyId: recieveData.storyId }}), 8 | newPost: () => { 9 | cacher.deleteCache('/story'); 10 | return JSON.stringify({ msgType: 'reloadPosts' }); 11 | }, 12 | newFeedback: () => JSON.stringify({ msgType: 'reloadFeedbacks' }) 13 | }; 14 | 15 | const wsController = (ws, logger) => { 16 | ws.on('connection', (connection) => { 17 | connection.on('error', logger.error); 18 | connection.on('message', (msg) => { 19 | try { 20 | msg = JSON.parse(msg); 21 | const message = msgTypes[msg.msgType](msg.data); 22 | for (const client of ws.clients) { 23 | if (client.readyState === WebSocket.OPEN) client.send(message, { binary: false }); 24 | } 25 | } catch (err) { 26 | logger.error(err); 27 | } 28 | }); 29 | }); 30 | }; 31 | 32 | module.exports = { wsController }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Bogdan Yarmolka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /adminPanel/src/components/Login.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 |
14 | 15 |
16 | 17 |
18 | 19 |
20 |
21 | 22 | -------------------------------------------------------------------------------- /backend/config/config.js: -------------------------------------------------------------------------------- 1 | const PORT = process.env.PORT ?? 3000; 2 | const HOST = process.env.HOST ?? '127.0.0.1'; 3 | const PROTOCOL = process.env.PROTOCOL; 4 | const DB_PORT = process.env.DB_PORT; 5 | const DB_NAME = process.env.DB_NAME; 6 | const DB_USER = process.env.DB_USER; 7 | const DB_PASSWORD = process.env.DB_PASSWORD; 8 | const OPENAI_TOKEN = process.env.OPENAI_TOKEN; 9 | const GPT_MODEL_TEXT = process.env.GPT_MODEL_TEXT; 10 | const GPT_MODEL_IMAGE = process.env.GPT_MODEL_IMAGE; 11 | const TIME_ZONE = process.env.TIME_ZONE; 12 | const POSTING_STORY_TIME = process.env.POSTING_STORY_TIME; 13 | const POSTING_INTERVAL = process.env.POSTING_INTERVAL; 14 | const IMAGE_QUALITY = process.env.IMAGE_QUALITY; 15 | const CACHE_LIMIT_ITEMS = process.env.CACHE_LIMIT_ITEMS; 16 | const RENEW_TOKEN_TIME = process.env.RENEW_TOKEN_TIME; 17 | const ADMIN_LOGIN = process.env.ADMIN_LOGIN; 18 | const ADMIN_PASS = process.env.ADMIN_PASS; 19 | 20 | module.exports = { 21 | PORT, 22 | HOST, 23 | PROTOCOL, 24 | DB_PORT, 25 | DB_NAME, 26 | DB_USER, 27 | DB_PASSWORD, 28 | OPENAI_TOKEN, 29 | GPT_MODEL_TEXT, 30 | GPT_MODEL_IMAGE, 31 | TIME_ZONE, 32 | POSTING_STORY_TIME, 33 | POSTING_INTERVAL, 34 | IMAGE_QUALITY, 35 | CACHE_LIMIT_ITEMS, 36 | RENEW_TOKEN_TIME, 37 | ADMIN_LOGIN, 38 | ADMIN_PASS 39 | }; -------------------------------------------------------------------------------- /backend/logger/logger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('node:path'); 4 | const { appendFile, createDir } = require('../fs/fs.js'); 5 | 6 | class Logger { 7 | constructor(dirName) { 8 | this.dirName = dirName; 9 | return this.init(); 10 | } 11 | 12 | async init() { 13 | await createDir(path.join(this.dirName, '..')); 14 | await createDir(this.dirName); 15 | await createDir(path.join(this.dirName, 'error')); 16 | await createDir(path.join(this.dirName, 'logs')); 17 | return this; 18 | } 19 | 20 | async log(message) { 21 | const date = new Date(); 22 | const fileName = `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}.log`; 23 | const filePath = path.resolve(this.dirName, 'logs', fileName); 24 | const data = `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()} ${message}\n`; 25 | await appendFile(filePath, data); 26 | } 27 | 28 | async error(err) { 29 | const date = new Date(); 30 | const fileName = `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}.log`; 31 | const filePath = path.resolve(this.dirName, 'error', fileName); 32 | const data = `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()} ${err.message}\n`; 33 | await appendFile(filePath, data); 34 | } 35 | } 36 | 37 | module.exports = { Logger }; -------------------------------------------------------------------------------- /backend/controllers/static.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('node:path'); 4 | const { readFile, isFileExist } = require('../fs/fs.js'); 5 | const { cacher } = require('../cacher/cacherSingleton.js'); 6 | const { MIME_TYPES } = require('../mimeTypes/mimetypes.js'); 7 | 8 | const STATIC_PATH = path.resolve(__dirname, '..', 'static'); 9 | 10 | const pathes = { 11 | '/': '/index.html', 12 | '/article': '/index.html', 13 | '/about': '/index.html', 14 | '/feedback': '/index.html', 15 | '/adminPanel': '/adminPanel.html' 16 | }; 17 | 18 | const staticController = async (req, res, logger) => { 19 | req.url = pathes[req.url] ? pathes[req.url] : req.url.startsWith('/article') ? pathes['/article'] : req.url; 20 | const paths = [STATIC_PATH, req.url]; 21 | const filePath = path.join(...paths); 22 | if (!await isFileExist(filePath)) { 23 | res.writeHead(404); 24 | return void res.end('Not found'); 25 | } 26 | try { 27 | const data = await readFile(filePath); 28 | const ext = path.extname(filePath).substring(1).toLowerCase(); 29 | const mimeType = MIME_TYPES[ext]; 30 | res.writeHead(200, { 'Content-Type': mimeType }); 31 | res.end(data); 32 | const cache = { data, mimeType }; 33 | cacher.setCache(req.url, cache); 34 | } catch (err) { 35 | res.writeHead(500); 36 | res.end('Something went wrong'); 37 | await logger.error(err); 38 | } 39 | }; 40 | 41 | module.exports = { staticController }; -------------------------------------------------------------------------------- /adminPanel/src/components/Comment.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 |
22 |

Name: {commentData.username}

23 |

Story id: {commentData.story_id}

24 |

Text: {commentData.comment_text}

25 |
26 | 27 |
28 | {formatDateTime(commentData.created_at)} 29 |
30 |
31 | 32 | -------------------------------------------------------------------------------- /backend/controllers/login.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { RENEW_TOKEN_TIME, ADMIN_LOGIN, ADMIN_PASS } = require('../config/config'); 4 | 5 | const generateToken = () => { 6 | return Math.random() + Date.now() + Math.random(); 7 | }; 8 | 9 | let token = generateToken(); 10 | 11 | setInterval(() => { 12 | token = generateToken(); 13 | }, RENEW_TOKEN_TIME); 14 | 15 | const isUserLogined = (cookies) => cookies && cookies.loginToken && +cookies.loginToken === token; 16 | 17 | const loginUser = async (req, res, logger, body) => { 18 | try { 19 | if (body.login === ADMIN_LOGIN && body.password === ADMIN_PASS) { 20 | res.setHeader('Set-Cookie', `loginToken=${token}; Secure; HttpOnly`); 21 | res.writeHead(200, { 'Content-Type': 'text/plain' }); 22 | res.end(); 23 | } else { 24 | res.writeHead(400); 25 | res.end(); 26 | } 27 | logger.log(`connected: ${req.connection.remoteAddress}`); 28 | } catch (err) { 29 | res.writeHead(500); 30 | res.end('Something went wrong'); 31 | await logger.error(err); 32 | } 33 | }; 34 | 35 | const checkLogin = (req, res, logger, body, cookies) => { 36 | try { 37 | if (isUserLogined(cookies)) { 38 | res.writeHead(200); 39 | res.end('logined'); 40 | } else { 41 | res.writeHead(400); 42 | res.end('no logined'); 43 | } 44 | } catch (err) { 45 | res.writeHead(500); 46 | res.end('Something went wrong'); 47 | logger.error(err); 48 | } 49 | }; 50 | 51 | module.exports = { loginUser, isUserLogined, checkLogin }; -------------------------------------------------------------------------------- /adminPanel/src/components/Story.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 |
22 |

Title: {storyData.title}

23 |

Story id: {storyData.story_id}

24 |

25 |
26 | 27 |
28 | {formatDateTime(storyData.created_at)} 29 |
30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Jungle Blog 2 | Welcome to Jungle Blog! We appreciate your interest and encourage contributions from the community. Whether you are a developer, designer, tester, or someone with valuable feedback, your collaboration is valuable. 3 | 4 | ## How to Contribute 5 | ### Reporting Issues 6 | If you encounter bugs or have suggestions, please open an issue on our [issue tracker](../../issues). Provide detailed information about the problem and steps to reproduce it. Screenshots and code snippets are often helpful. 7 | 8 | ### Feature Requests 9 | If you have an idea for a new feature, please open an issue to discuss it. We welcome input on potential enhancements. 10 | 11 | ## Code Contributions 12 | 1. Fork the repository. 13 | 2. Create a new branch for your feature or bug fix. 14 | 3. Make your changes. 15 | 4. Test thoroughly. 16 | 5. Ensure your code adheres to the project's coding standards. 17 | 6. Submit a pull request. 18 | 19 | ### Documentation 20 | Improvements to documentation are always appreciated. If you find areas that need clarification or additional information, please update the relevant documentation files. 21 | 22 | ## Code of Conduct 23 | Please note that Jungle Blog has a [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you agree to abide by its terms. 24 | 25 | ## Development Setup 26 | Provide instructions on setting up a development environment, dependencies, and any specific configurations needed for contributors. 27 | 28 | ## Acknowledgments 29 | Thank you for considering contributing to JungleBlog! Your involvement helps make this project vibrant and successful. 30 | 31 | -------------------------------------------------------------------------------- /backend/controllers/feedback.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { pool } = require('../db/pool.js'); 4 | const { isUserLogined } = require('./login.js'); 5 | const { cacher } = require('../cacher/cacherSingleton.js'); 6 | const { MIME_TYPES } = require('../mimeTypes/mimetypes.js'); 7 | 8 | const universalController = async (req, res, logger, query, queryData) => { 9 | try { 10 | const client = await pool.connect(); 11 | const data = await client.query(query, queryData); 12 | const resData = JSON.stringify(data.rows); 13 | const mimeType = MIME_TYPES['json']; 14 | res.writeHead(200, { 'Content-Type': mimeType }); 15 | res.end(resData); 16 | const cache = { data: resData, mimeType }; 17 | cacher.setCache(req.url, cache); 18 | client.release(); 19 | if (req.method === 'GET') { 20 | const cache = { data: resData, mimeType }; 21 | cacher.setCache(req.url, cache); 22 | } else { 23 | cacher.deleteCache(`${req.url}/feedbacks`); 24 | cacher.deleteCache(req.url); 25 | } 26 | } catch (err) { 27 | res.writeHead(500); 28 | res.end('Something went wrong'); 29 | await logger.error(err); 30 | } 31 | }; 32 | 33 | const getFeedbacks = async (req, res, logger, body, cookies) => { 34 | if (!isUserLogined(cookies)) { 35 | res.writeHead(401); 36 | return void res.end('Not authorised'); 37 | } 38 | const query = 'SELECT * FROM jungleBlog.feedbacks ORDER BY created_at DESC'; 39 | await universalController(req, res, logger, query); 40 | }; 41 | 42 | const createFeedback = async (req, res, logger, body) => { 43 | const query = 'INSERT INTO jungleblog.feedbacks(name, text) VALUES($1, $2) RETURNING feedback_id, name, text'; 44 | const queryData = [body.name, body.text]; 45 | await universalController(req, res, logger, query, queryData); 46 | }; 47 | 48 | module.exports = { getFeedbacks, createFeedback }; -------------------------------------------------------------------------------- /adminPanel/src/assets/svelte.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /jungleBlog/src/assets/svelte.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /jungleBlog/src/components/Card.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 |
11 |
12 |

{shortenedTitle}

13 |
14 |
15 | {article.title} 16 |
17 |
18 | 19 | 77 | 78 | -------------------------------------------------------------------------------- /adminPanel/src/components/Navigation.svelte: -------------------------------------------------------------------------------- 1 | 33 | 34 | 44 | 45 | -------------------------------------------------------------------------------- /jungleBlog/src/components/Main.svelte: -------------------------------------------------------------------------------- 1 | 35 | 36 |
37 | 38 |
39 | {#each $articlesData as article} 40 | 41 | 42 | 43 | {/each} 44 |
45 |
46 | 47 | -------------------------------------------------------------------------------- /jungleBlog/src/app.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-synthesis: none; 3 | text-rendering: optimizeLegibility; 4 | -webkit-font-smoothing: antialiased; 5 | -moz-osx-font-smoothing: grayscale; 6 | -webkit-text-size-adjust: 100%; 7 | 8 | --header-background-color: #1a4814; 9 | --header-color: white; 10 | 11 | --main-background-color: #eaffe0; 12 | --main-color: #001419; 13 | 14 | --card-background-color: #c8eac1; 15 | --card-line-color: #1A4814; 16 | 17 | --form-section-background: #d6e5d3; 18 | --form-color: black; 19 | --form-placeholer-color: grey; 20 | 21 | --comments-background: #c8eac1; 22 | 23 | --button-hover-color: #a49f9f; 24 | } 25 | 26 | [data-theme="dark"] { 27 | --header-background-color: #1F3329; 28 | --header-color: #EAFFE0; 29 | 30 | --main-background-color: #344E41; 31 | --main-color: #EAFFE0; 32 | 33 | --card-background-color: #588157; 34 | --card-line-color: #EAFFE0; 35 | 36 | --form-section-background: #87A186; 37 | --form-color: #EAFFE0; 38 | --form-placeholer-color: #cfe5cc; 39 | 40 | --comments-background: #588157; 41 | 42 | --button-hover-color: #588157; 43 | } 44 | 45 | body{ 46 | font-family: 'Inknut Antiqua', serif; 47 | line-height: 1.5; 48 | font-weight: 400; 49 | 50 | color-scheme: light dark; 51 | 52 | background-color: var(--main-background-color); 53 | color: var(--main-color); 54 | 55 | cursor: url("/res/cursor.png"), auto; 56 | 57 | margin: 0; 58 | padding: 0; 59 | display: flex; 60 | flex-direction: column; 61 | min-height: 100vh; 62 | } 63 | 64 | a, button, input[type="submit"], input[type="button"], label[for] { 65 | cursor: url("/res/pointer.png"), pointer; 66 | } 67 | 68 | a { 69 | text-decoration: none; 70 | color: inherit; 71 | } 72 | 73 | ::-webkit-scrollbar { 74 | width: 12px; 75 | height: 12px; 76 | } 77 | 78 | ::-webkit-scrollbar-track { 79 | background: #a6c3a0; 80 | } 81 | 82 | ::-webkit-scrollbar-thumb { 83 | background-color: var(--header-background-color); 84 | border-radius: 6px; 85 | border: 3px solid #a6c3a0; 86 | } 87 | 88 | ::-webkit-scrollbar-thumb:hover { 89 | background: #123512; 90 | } 91 | 92 | :root { 93 | --scrollbar-width: calc(100vw - 100%); 94 | } 95 | header { 96 | margin-right: calc(-1*var(--scrollbar-width)); 97 | } 98 | 99 | body, html{ 100 | overflow-x: hidden; 101 | } -------------------------------------------------------------------------------- /backend/controllers/story.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { pool } = require('../db/pool.js'); 4 | const { isUserLogined } = require('./login.js'); 5 | const { cacher } = require('../cacher/cacherSingleton.js'); 6 | const { MIME_TYPES } = require('../mimeTypes/mimetypes.js'); 7 | 8 | const universalController = async (req, res, logger, query, queryData, storyId) => { 9 | try { 10 | const client = await pool.connect(); 11 | const data = await client.query(query, queryData); 12 | const resData = JSON.stringify(data.rows); 13 | const mimeType = MIME_TYPES['json']; 14 | res.writeHead(200, { 'Content-Type': mimeType }); 15 | client.release(); 16 | if (req.method === 'GET') { 17 | const cache = { data: resData, mimeType }; 18 | cacher.setCache(req.url, cache); 19 | } else { 20 | cacher.deleteCache(`${req.url}/${storyId}`); 21 | cacher.deleteCache(req.url); 22 | } 23 | res.end(resData); 24 | } catch (err) { 25 | res.writeHead(500); 26 | res.end('Something went wrong'); 27 | await logger.error(err); 28 | } 29 | }; 30 | 31 | const getStories = async (req, res, logger) => { 32 | const query = 'SELECT story_id, title, content, image_url, created_at FROM jungleBlog.stories ORDER BY story_id'; 33 | await universalController(req, res, logger, query); 34 | }; 35 | 36 | const getStory = async (req, res, logger) => { 37 | const splitedUrl = req.url.split('/'); 38 | const storyId = splitedUrl[splitedUrl.length - 1]; 39 | const parsedStoryId = +storyId; 40 | if (isNaN(parsedStoryId)) { 41 | res.writeHead(400); 42 | return void res.end(`Invalid story id, id = "${storyId}"`); 43 | } 44 | const query = `SELECT story_id, title, content, image_url FROM jungleBlog.stories WHERE story_id=${storyId}`; 45 | await universalController(req, res, logger, query); 46 | }; 47 | 48 | const updateStory = async (req, res, logger, body, cookies) => { 49 | if (!isUserLogined(cookies)) { 50 | res.writeHead(401); 51 | return void res.end('Not authorised'); 52 | } 53 | const storyId = body.story_id; 54 | if (isNaN(+storyId)) { 55 | res.writeHead(400); 56 | return void res.end(`Invalid story id, id = "${storyId}"`); 57 | } 58 | const query = `UPDATE jungleBlog.stories SET content = $1 WHERE story_id = ${storyId}`; 59 | const queryData = [body.content]; 60 | await universalController(req, res, logger, query, queryData, storyId); 61 | }; 62 | 63 | module.exports = { getStories, getStory, updateStory }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jungle Blog 2 | ### Jungle Blog - ChatGPT-powered Web-Blog Documentation 3 | Welcome to the Jungle Blog GitHub repository! Jungle Blog is an innovative web-blog project that harnesses the capabilities of ChatGPT to create cool and engaging stories. This documentation provides essential information to help you understand, contribute, and deploy Jungle Blog. 4 | 5 | ## Table of Contents 6 | 1. [Introduction](#introduction) 7 | 2. [Getting Started](#getting-started) 8 | * [Prerequisites](#prerequisites) 9 | * [Installation](#installation) 10 | 3. [Usage](#usage) 11 | * [Configuring ChatGPT](#configuring-chatgpt) 12 | * [Create DataBase](#creating-data-base) 13 | * [Deploying Jungle Blog](#deploying-jungle-blog) 14 | 4. [Contributing](#contributing) 15 | 5. [Issues and Bug Reports](#issues-and-bug-reports) 16 | 6. [License](#license) 17 | 18 | ## Introduction 19 | The motivation for creating this project was to explore the possibilities of the gpt chat API and various tools for working as a software engineer. 20 | 21 | ## Getting Started 22 | 23 | ### Prerequisites: 24 | * Node JS 25 | * PostgreSQL 26 | * ChatGPT API key (Get yours at OpenAI) 27 | 28 | ### Installation 29 | 1. Clone the Jungle Blog repository: 30 | ```bash 31 | git clone https://github.com/thebaldehit/jungleBlog.git 32 | cd jungleBlog 33 | ``` 34 | 2. Install dependencies: 35 | ```bash 36 | cd backend 37 | npm i 38 | cd ../jungleBlog 39 | npm i 40 | cd ../adminPanel 41 | npm i 42 | ``` 43 | 44 | ## Usage 45 | ### Configuring ChatGPT 46 | 1. Obtain a ChatGPT API key from [OpenAI](https://platform.openai.com/signup/). 47 | 2. Create **.env** file from **.env.sample** 48 | 3. Setup **.env** file with your data (copy the token from the first subsection to .env ) 49 | 50 | ### Create DataBase 51 | 1. Run **create.js** script to create DataBase (before it run Postgres service): 52 | ```bash 53 | cd backend/db 54 | node create.js 55 | ``` 56 | 57 | ### Deploying Jungle Blog 58 | 1. Create **.env** file from **.env.sample** (if you have not done it yet) 59 | 2. Fill data to **.env** (fill all fields otherwise app will not work) 60 | 3. Run up: 61 | ```bash 62 | cd backend 63 | node index.js 64 | ``` 65 | 66 | ## Contributing 67 | We welcome contributions! Please follow our [contribution guidelines](CONTRIBUTING.md) to get started. 68 | 69 | ## Issues and Bug Reports 70 | If you encounter any issues or want to report a bug, please open an issue on the [issue tracker](../../issues). 71 | 72 | ## License 73 | Jungle Blog is licensed under the [MIT License](LICENSE). 74 | 75 | Thank you for being part of the Jungle Blog community! Happy coding! 🌿📝 -------------------------------------------------------------------------------- /adminPanel/README.md: -------------------------------------------------------------------------------- 1 | # Svelte + Vite 2 | 3 | This template should help get you started developing with Svelte in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). 8 | 9 | ## Need an official Svelte framework? 10 | 11 | Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more. 12 | 13 | ## Technical considerations 14 | 15 | **Why use this over SvelteKit?** 16 | 17 | - It brings its own routing solution which might not be preferable for some users. 18 | - It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app. 19 | 20 | This template contains as little as possible to get started with Vite + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project. 21 | 22 | Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate. 23 | 24 | **Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?** 25 | 26 | Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information. 27 | 28 | **Why include `.vscode/extensions.json`?** 29 | 30 | Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project. 31 | 32 | **Why enable `checkJs` in the JS template?** 33 | 34 | It is likely that most cases of changing variable types in runtime are likely to be accidental, rather than deliberate. This provides advanced typechecking out of the box. Should you like to take advantage of the dynamically-typed nature of JavaScript, it is trivial to change the configuration. 35 | 36 | **Why is HMR not preserving my local component state?** 37 | 38 | HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/sveltejs/svelte-hmr/tree/master/packages/svelte-hmr#preservation-of-local-state). 39 | 40 | If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR. 41 | 42 | ```js 43 | // store.js 44 | // An extremely simple external store 45 | import { writable } from 'svelte/store' 46 | export default writable(0) 47 | ``` 48 | -------------------------------------------------------------------------------- /jungleBlog/README.md: -------------------------------------------------------------------------------- 1 | # Svelte + Vite 2 | 3 | This template should help get you started developing with Svelte in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). 8 | 9 | ## Need an official Svelte framework? 10 | 11 | Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more. 12 | 13 | ## Technical considerations 14 | 15 | **Why use this over SvelteKit?** 16 | 17 | - It brings its own routing solution which might not be preferable for some users. 18 | - It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app. 19 | 20 | This template contains as little as possible to get started with Vite + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project. 21 | 22 | Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate. 23 | 24 | **Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?** 25 | 26 | Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information. 27 | 28 | **Why include `.vscode/extensions.json`?** 29 | 30 | Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project. 31 | 32 | **Why enable `checkJs` in the JS template?** 33 | 34 | It is likely that most cases of changing variable types in runtime are likely to be accidental, rather than deliberate. This provides advanced typechecking out of the box. Should you like to take advantage of the dynamically-typed nature of JavaScript, it is trivial to change the configuration. 35 | 36 | **Why is HMR not preserving my local component state?** 37 | 38 | HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/sveltejs/svelte-hmr/tree/master/packages/svelte-hmr#preservation-of-local-state). 39 | 40 | If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR. 41 | 42 | ```js 43 | // store.js 44 | // An extremely simple external store 45 | import { writable } from 'svelte/store' 46 | export default writable(0) 47 | ``` 48 | -------------------------------------------------------------------------------- /jungleBlog/src/components/AboutUs.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
15 | {#if showContent} 16 |

About My Blog

17 |
18 |
19 |

Hello, my brave readers!

20 |

I'm a beginner in the world of survival, and this is my blog where I share my unique experience of surviving on a deserted island in the jungle. 21 | Why am I here? I'm seeking adventure, testing myself, learning skills forgotten by the modern world, and of course, sharing it all with you.

22 |

Every day here is a new challenge. I write about how I build shelters from what I find in the jungle, how I find and purify water, gather food, or even try hunting. 23 | I share my successes and failures, moments of joy and despair. But this blog isn't just about survival. It's also about connecting with nature, self-discovery, and 24 | finding inner strength. I explore the environment, observe wildlife, try to understand the island's ecosystem, and my place in it. In this blog, you'll find not 25 | only a description of my adventures but also photos, survival tips, information about the plants and animals I encounter, and much more. I want to share everything 26 | I learn with you and possibly inspire someone for their own adventures.

27 |

So, if you're interested in nature, survival, or just want to feel part of something extraordinary, welcome to my blog. Join my adventure and discover the unknown 28 | with me!

29 |
30 |
31 | aboutUsImg 32 |
33 |
34 | {/if} 35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /backend/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('dotenv').config(); 4 | const WebSocket = require('ws'); 5 | const http = require('node:http'); 6 | const path = require('node:path'); 7 | const { PORT } = require('./config/config.js'); 8 | const { Logger } = require('./logger/logger.js'); 9 | const { wsController } = require('./controllers/ws.js'); 10 | const { cacher } = require('./cacher/cacherSingleton.js'); 11 | const { staticController } = require('./controllers/static.js'); 12 | const { loginUser, checkLogin } = require('./controllers/login.js'); 13 | const { getFeedbacks, createFeedback } = require('./controllers/feedback.js'); 14 | const { getStories, getStory, updateStory } = require('./controllers/story.js'); 15 | const { getAllComments, getCommentsByStoryId, createComment, deleteComment } = require('./controllers/comment.js'); 16 | 17 | let logger; 18 | 19 | const routing = { 20 | '/story': { 21 | GET: getStories, 22 | PATCH: updateStory 23 | }, 24 | '/story/.*': { GET: getStory }, 25 | '/comment': { 26 | GET: getAllComments, 27 | POST: createComment, 28 | DELETE: deleteComment 29 | }, 30 | '/comment/.*': { GET: getCommentsByStoryId }, 31 | '/feedbacks': { 32 | GET: getFeedbacks, 33 | POST: createFeedback 34 | }, 35 | '/login': { 36 | POST: loginUser, 37 | GET: checkLogin 38 | } 39 | }; 40 | 41 | const bodyParser = async (req) => { 42 | const data = []; 43 | for await (const chunk of req) { 44 | data.push(chunk); 45 | } 46 | const stringData = Buffer.concat(data).toString(); 47 | if (!stringData) return; 48 | return JSON.parse(stringData); 49 | }; 50 | 51 | const cookiesParser = (req) => { 52 | if (!req.headers.cookie) return; 53 | const splited = req.headers.cookie.split(';'); 54 | const cookies = {}; 55 | for (const item of splited) { 56 | const [name, value] = item.trim().split('='); 57 | cookies[name] = value; 58 | } 59 | return cookies; 60 | }; 61 | 62 | const rxRouting = []; 63 | for (const key in routing) { 64 | if (key.includes('*')) { 65 | const rx = new RegExp(key); 66 | const route = routing[key]; 67 | rxRouting.push([ rx, route ]); 68 | delete routing[key]; 69 | } 70 | } 71 | 72 | const server = http.createServer(async (req, res) => { 73 | if (req.method === 'OPTIONS') { 74 | res.writeHead(200); 75 | return void res.end(); 76 | } 77 | const cache = cacher.getCache(req.url); 78 | if (cache && req.method === 'GET') { 79 | res.writeHead(200, { 'Content-Type': cache.mimeType }); 80 | return void res.end(cache.data); 81 | } 82 | let methods = routing[req.url]; 83 | if (!methods) { 84 | for (const rx of rxRouting) { 85 | if (req.url.match(rx[0])) { 86 | methods = rx[1]; 87 | } 88 | } 89 | } 90 | if (!methods) staticController(req, res, logger); 91 | else { 92 | const body = await bodyParser(req); 93 | const cookies = cookiesParser(req); 94 | const controller = methods[req.method]; 95 | if (controller) return void controller(req, res, logger, body, cookies); 96 | res.writeHead(400); 97 | res.end('No such path'); 98 | } 99 | }); 100 | 101 | server.listen(PORT, async () => { 102 | logger = await new Logger(path.resolve(__dirname, 'logs', 'server')); 103 | const ws = new WebSocket.Server({ server }); 104 | wsController(ws, logger); 105 | console.log(`Starting on ${PORT}...`); 106 | }); -------------------------------------------------------------------------------- /backend/controllers/comment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { pool } = require('../db/pool.js'); 4 | const { HOST, PROTOCOL } = require('../config/config.js'); 5 | const { isUserLogined } = require('./login.js'); 6 | const { cacher } = require('../cacher/cacherSingleton.js'); 7 | const { MIME_TYPES } = require('../mimeTypes/mimetypes.js'); 8 | 9 | const isBadId = (id) => isNaN(id); 10 | 11 | const getStoryIdFromUrl = (url) => { 12 | const splitedUrl = url.split('/'); 13 | const storyId = splitedUrl[splitedUrl.length - 1]; 14 | return +storyId; 15 | }; 16 | 17 | const getParamsFromUrl = (url) => new URL(url, `${PROTOCOL}://${HOST}`); 18 | 19 | const universalController = async (req, res, logger, query, code, queryData) => { 20 | try { 21 | const client = await pool.connect(); 22 | const data = await client.query(query, queryData); 23 | if (req.method === 'DELETE' && data.rows.length === 0) { 24 | res.writeHead(400); 25 | return void res.end('Bad request'); 26 | } 27 | const resData = JSON.stringify(data.rows); 28 | const mimeType = MIME_TYPES['json']; 29 | res.writeHead(code ? code : 200, { 'Content-Type': mimeType }); 30 | res.end(resData); 31 | client.release(); 32 | if (req.method === 'GET') { 33 | const cache = { data: resData, mimeType }; 34 | cacher.setCache(req.url, cache); 35 | } else { 36 | cacher.deleteCache(`${req.url}/${data.rows[0].story_id}`); 37 | cacher.deleteCache(req.url); 38 | } 39 | } catch (err) { 40 | res.writeHead(500); 41 | res.end('Something went wrong'); 42 | await logger.error(err); 43 | } 44 | }; 45 | 46 | 47 | const getAllComments = async (req, res, logger) => { 48 | const query = 'SELECT comment_id, story_id, username, comment_text, created_at FROM jungleBlog.comments ORDER BY created_at'; 49 | await universalController(req, res, logger, query); 50 | }; 51 | 52 | const getCommentsByStoryId = async (req, res, logger) => { 53 | const urlParams = getParamsFromUrl(req.url); 54 | const storyId = getStoryIdFromUrl(urlParams.pathname); 55 | if (isBadId(storyId)) { 56 | res.writeHead(400); 57 | return res.end(`Invalid story id in getting comments, id = "${storyId}"`); 58 | } 59 | const starPos = +urlParams.searchParams.get('start'); 60 | const endPos = +urlParams.searchParams.get('end'); 61 | let query = `SELECT comment_id, username, comment_text, created_at FROM jungleBlog.comments WHERE story_id=${storyId} ORDER BY created_at DESC`; 62 | if (!!starPos && !isNaN(starPos)) query += ` OFFSET ${starPos}`; 63 | if (!!endPos && !isNaN(endPos)) query += ` LIMIT ${endPos - starPos}`; 64 | await universalController(req, res, logger, query); 65 | }; 66 | 67 | const createComment = async (req, res, logger, body) => { 68 | const storyId = body.story_id; 69 | if (isBadId(storyId)) { 70 | res.writeHead(400); 71 | return res.end(`Invalid story id, id = "${storyId}"`); 72 | } 73 | const queryData = [body.story_id, body.username, body.comment_text]; 74 | const query = 'INSERT INTO jungleblog.comments(story_id, username, comment_text) VALUES ($1, $2, $3) RETURNING username, comment_text, created_at, story_id'; 75 | await universalController(req, res, logger, query, 201, queryData); 76 | }; 77 | 78 | const deleteComment = async (req, res, logger, body, cookies) => { 79 | if (!isUserLogined(cookies)) { 80 | res.writeHead(401); 81 | return void res.end('Not authorised'); 82 | } 83 | const commentId = body.comment_id; 84 | if (isBadId(commentId)) { 85 | res.writeHead(400); 86 | return res.end(`Invalid comment id, id = "${commentId}"`); 87 | } 88 | const query = `DELETE FROM jungleblog.comments WHERE comment_id = ${commentId} RETURNING story_id`; 89 | await universalController(req, res, logger, query, 204); 90 | }; 91 | 92 | module.exports = { getAllComments, getCommentsByStoryId, createComment, deleteComment }; -------------------------------------------------------------------------------- /backend/contentGenerator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('dotenv').config(); 4 | const WebSocket = require('ws'); 5 | const OpenAI = require('openai'); 6 | const path = require('node:path'); 7 | const { pool } = require('./db/pool.js'); 8 | const { Logger } = require('./logger/logger.js'); 9 | const { writeFile, createDir } = require('./fs/fs.js'); 10 | const storiesTheme = require('./data/thmesForStories.json'); 11 | const themeIndexObj = require('./data/nextThemeIndex.json'); 12 | const { generateText, genetateImage } = require('./gpt-api/gptApi.js'); 13 | const { requestBenchMark, convertFromB64toBuffer } = require('./gpt-api/toolsesForApi.js'); 14 | const { OPENAI_TOKEN, GPT_MODEL_TEXT, GPT_MODEL_IMAGE, TIME_ZONE, POSTING_STORY_TIME, IMAGE_QUALITY, PORT, POSTING_INTERVAL } = require('./config/config.js'); 15 | 16 | const openai = new OpenAI({ 17 | apiKey: OPENAI_TOKEN 18 | }); 19 | 20 | const getMilisecondsToHour = (hour, timeZone) => { 21 | const targettTime = new Date(); 22 | targettTime.setDate(targettTime.getDate() + 1); 23 | targettTime.setHours(hour, 0, 0, 0); 24 | targettTime.toLocaleString({ timeZone }); 25 | return targettTime.getTime() - Date.now(); 26 | }; 27 | 28 | const notifyAboutNewPost = () => { 29 | const socket = new WebSocket(`ws://localhost:${PORT}`); 30 | socket.on('open', () => { 31 | socket.send(JSON.stringify({ msgType: 'newPost' })); 32 | socket.close(); 33 | }); 34 | }; 35 | 36 | const generateStory = async (logger) => { 37 | try { 38 | const themeIndex = themeIndexObj.themeIndex++; 39 | const theme = storiesTheme[themeIndex]; 40 | const messages = [ 41 | { 'role': 'system', 'content': `Write a first-person narrative as a part of notes entry for ${themeIndex} of surviving on a deserted island. Describe detailing the personal experiences, challenges faced, and survival strategies used` }, 42 | { 'role': 'user', 'content': `Write a first-person narrative as a diary entry for ${theme.title} of surviving on a deserted island. Describe ${theme.content}, detailing the personal experiences, challenges faced, and survival strategies used` } 43 | ]; 44 | const textParams = [openai, GPT_MODEL_TEXT, messages]; 45 | const imgParams = [openai, GPT_MODEL_IMAGE, theme.imagePrompt, 1, IMAGE_QUALITY]; 46 | const textBenchMark = await requestBenchMark(generateText, ...textParams); 47 | const imageBanchMark = await requestBenchMark(genetateImage, ...imgParams); 48 | await logger.log('Text generation: ' + textBenchMark.deltaTime); 49 | await logger.log('Image generation: ' + imageBanchMark.deltaTime); 50 | const imageBuffer = convertFromB64toBuffer(imageBanchMark.data['b64_json']); 51 | const imageUrl = `res/themeImg/${themeIndex + 1}.jpg`; 52 | const client = await pool.connect(); 53 | const query = 'INSERT INTO jungleBlog.stories(title, content, image_url) VALUES ($1, $2, $3)'; 54 | await createDir(path.join(__dirname, 'static', 'res', 'themeImg')); 55 | await writeFile(`./static/res/themeImg/${themeIndex + 1}.jpg`, imageBuffer); 56 | await writeFile('./data/nextThemeIndex.json', JSON.stringify(themeIndexObj)); 57 | await client.query(query, [theme.title, textBenchMark.data, imageUrl]); 58 | client.release(); 59 | notifyAboutNewPost(); 60 | } catch (err) { 61 | await logger.error(err); 62 | } 63 | }; 64 | 65 | const startGenerateContent = async () => { 66 | const logger = await new Logger(path.join(__dirname, 'logs', 'gptLogic')); 67 | const milisecondsToStart = getMilisecondsToHour(POSTING_STORY_TIME, TIME_ZONE); 68 | setTimeout(() => { 69 | generateStory(logger); 70 | const intervalTimer = setInterval(() => { 71 | if (!storiesTheme[themeIndexObj.themeIndex]) { 72 | clearInterval(intervalTimer); 73 | } else { 74 | generateStory(logger); 75 | } 76 | }, POSTING_INTERVAL); 77 | }, milisecondsToStart); 78 | }; 79 | 80 | module.exports = { startGenerateContent }; -------------------------------------------------------------------------------- /jungleBlog/src/components/Feedback.svelte: -------------------------------------------------------------------------------- 1 | 63 | 64 |
65 | {#if showContent} 66 |

Feedback

67 |

You can ask questions or leave feedback here. We are glad to hear from you.

68 |
69 | 70 | 71 |
72 | 73 |
74 |
75 | {/if} 76 |
77 | 78 | 151 | -------------------------------------------------------------------------------- /jungleBlog/src/bannedWords.js: -------------------------------------------------------------------------------- 1 | export const bannedWords = ["4r5e", "5h1t", "5hit", "a55", "anal", "anus", "ar5e", "arrse", "arse", "ass", "ass-fucker", "asses", "assfucker", "assfukka", "asshole", 2 | "assholes", "asswhole", "a_s_s", "b!tch", "b00bs", "b17ch", "b1tch", "ballbag", "balls", "ballsack", "bastard", "beastial", "beastiality", "bellend", "bestial", 3 | "bestiality", "bi+ch", "biatch", "bitch", "bitcher", "bitchers", "bitches", "bitchin", "bitching", "blow job", "blowjob", "blowjobs", "boiolas", "bollock", 4 | "bollok", "boner", "boob", "boobs", "booobs", "boooobs", "booooobs", "booooooobs", "breasts", "buceta", "bugger", "bum", "bunny fucker", "butt", "butthole", "buttmuch", 5 | "buttplug", "c0ck", "c0cksucker", "carpet muncher", "cawk", "chink", "cipa", "cl1t", "clit", "clitoris", "clits", "cnut", "cock", "cock-sucker", "cockface", "cockhead", 6 | "cockmunch", "cockmuncher", "cocks", "cocksuck", "cocksucked", "cocksucker", "cocksucking", "cocksucks", "cocksuka", "cocksukka", "cok", "cokmuncher", "coksucka", 7 | "coon", "cox", "crap", "cum", "cummer", "cumming", "cums", "cumshot", "cunilingus", "cunillingus", "cunnilingus", "cunt", "cuntlick", "cuntlicker", "cuntlicking", "cunts", 8 | "cyalis", "cyberfuc", "cyberfuck", "cyberfucked", "cyberfucker", "cyberfuckers", "cyberfucking", "d1ck", "damn", "dick", "dickhead", "dildo", "dildos", "dink", "dinks", 9 | "dirsa", "dlck", "dog-fucker", "doggin", "dogging", "donkeyribber", "doosh", "duche", "dyke", "ejaculate", "ejaculated", "ejaculates", "ejaculating", "ejaculatings", 10 | "ejaculation", "ejakulate", "f u c k", "f u c k e r", "f4nny", "fag", "fagging", "faggitt", "faggot", "faggs", "fagot", "fagots", "fags", "fanny", "fannyflaps", 11 | "fannyfucker", "fanyy", "fatass", "fcuk", "fcuker", "fcuking", "feck", "fecker", "felching", "fellate", "fellatio", "fingerfuck", "fingerfucked", "fingerfucker", 12 | "fingerfuckers", "fingerfucking", "fingerfucks", "fistfuck", "fistfucked", "fistfucker", "fistfuckers", "fistfucking", "fistfuckings", "fistfucks", "flange", "fook", 13 | "fooker", "fuck", "fucka", "fucked", "fucker", "fuckers", "fuckhead", "fuckheads", "fuckin", "fucking", "fuckings", "fuckingshitmotherfucker", "fuckme", "fucks", 14 | "fuckwhit", "fuckwit", "fudge packer", "fudgepacker", "fuk", "fuker", "fukker", "fukkin", "fuks", "fukwhit", "fukwit", "fux", "fux0r", "f_u_c_k", "gangbang", 15 | "gangbanged", "gangbangs", "gaylord", "gaysex", "goatse", "God", "god-dam", "god-damned", "goddamn", "goddamned", "hardcoresex", "heshe", "hoar", "hoare", 16 | "hoer", "homo", "hore", "horniest", "horny", "hotsex", "jack-off", "jackoff", "jap", "jerk-off", "jism", "jiz", "jizm", "jizz", "kawk", "knob", "knobead", "knobed", 17 | "knobend", "knobhead", "knobjocky", "knobjokey", "kock", "kondum", "kondums", "kumming", "kums", "kunilingus", "l3i+ch", "l3itch", "labia", "lust", 18 | "lusting", "m0f0", "m0fo", "m45terbate", "ma5terb8", "ma5terbate", "master-bate", "masterb8", "masterbat*", "masterbat3", "masterbate", "masterbation", 19 | "masterbations", "masturbate", "mo-fo", "mof0", "mofo", "mothafuck", "mothafucka", "mothafuckas", "mothafuckaz", "mothafucked", "mothafucker", "mothafuckers", 20 | "mothafuckin", "mothafucking", "mothafuckings", "mothafucks", "mother fucker", "motherfuck", "motherfucked", "motherfucker", "motherfuckers", "motherfuckin", 21 | "motherfucking", "motherfuckings", "motherfuckka", "motherfucks", "muff", "mutha", "muthafecker", "muthafuckker", "muther", "mutherfucker", "n1gga", "n1gger", 22 | "nigg3r", "nigg4h", "nigga", "niggah", "niggas", "niggaz", "nigger", "niggers", "nob", "nob jokey", "nobhead", "nobjocky", "nobjokey", "numbnuts", "nutsack", 23 | "orgasim", "orgasims", "orgasm", "orgasms", "p0rn", "pawn", "pecker", "penis", "penisfucker", "phonesex", "phuck", "phuk", "phuked", "phuking", "phukked", "phukking", 24 | "phuks", "phuq", "pigfucker", "pimpis", "piss", "pissed", "pisser", "pissers", "pisses", "pissflaps", "pissin", "pissing", "pissoff", "porn", "porno", "pornography", 25 | "pornos", "prick", "pricks", "pron", "pube", "pusse", "pussi", "pussies", "pussy", "pussys", "rectum", "retard", "rimjaw", "rimming", "s hit", "s.o.b.", "sadist", 26 | "schlong", "screwing", "scroat", "scrote", "scrotum", "semen", "sex", "sh!+", "sh!t", "sh1t", "shag", "shagger", "shaggin", "shagging", "shemale", "shi+", "shit", 27 | "shitdick", "shite", "shited", "shitey", "shitfuck", "shitfull", "shithead", "shiting", "shitings", "shits", "shitted", "shitter", "shitters", "shitting", "shittings", 28 | "shitty", "skank", "slut", "sluts", "smegma", "smut", "snatch", "son-of-a-bitch", "spunk", "s_h_i_t", "t1tt1e5", "t1tties", "teets", "teez", "testical", 29 | "testicle", "tit", "titfuck", "tits", "titt", "tittie5", "tittiefucker", "titties", "tittyfuck", "tittywank", "titwank", "tosser", "turd", "tw4t", "twat", "twathead", 30 | "twatty", "twunt", "twunter", "v14gra", "v1gra", "vagina", "viagra", "vulva", "w00se", "wang", "wank", "wanker", "wanky", "whoar", "whore", "willies", "willy", "xrated"]; -------------------------------------------------------------------------------- /jungleBlog/src/components/Content.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
15 |
16 |

Survival a Jungle

17 |

Within the jungle's heart, emerald leaves weave tales of mystery in nature's sacred hush.

18 |
19 |
20 |

Deserted Island

21 |

On the deserted island, golden sands meet beneath the boundless sky.

22 |
23 |
24 | 25 |
26 |
27 |

28 | On an island, sands embrace sea,
29 | Nature's whispers, tranquility.
30 | Palm trees dance, shadows play,
31 | Isolation's refuge, serenity's bay.
32 | Time pauses beneath the sky,
33 | Sun-kissed solitude, untie.
34 | Footprints weave a tale untold,
35 | Nature's sanctuary, stories unfold.
36 | Ocean breeze, golden sand,
37 | Deserted island, a tranquil stand.
38 | Whispers echo, waves conspire,
39 | Solitude's dance, nature's lyre.
40 |

41 |
42 |
43 | main img 44 |
45 |
46 |

Jungle whispers, wild and free,
47 | Life's dance beneath the green tree.
48 | Vines entwine, a verdant brace,
49 | Survival's trial, nature's grace.
50 | Roaring beasts in shadows hide,
51 | Eyes gleam with instinct's pride.
52 | Rivers whisper tales untold,
53 | Secrets in green, ancient and bold.
54 | Underneath the leafy sweep,
55 | Heart beats, nature's rhythmic keep.
56 | Adapt or falter, jungle's decree,
57 | Life's wild symphony, a jubilee. 58 |

59 |
60 |
61 | 62 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /jungleBlog/src/components/Header.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 |
20 | 25 | 31 |
32 | {#if isDark === 'dark'} 33 | 34 | {:else} 35 | 36 | {/if} 37 | 40 |
41 |
42 | 43 | 44 | 195 | -------------------------------------------------------------------------------- /backend/data/thmesForStories.json: -------------------------------------------------------------------------------- 1 | [{"title":"Day 1 - Building Shelter","content":"Today marks my first day on this deserted island. After the initial shock, I spent the day scouting the area. I found a suitable spot near a freshwater stream and started building a basic shelter using palm fronds and sticks. It's rudimentary, but it's a start.","imagePrompt":"Realistic first person image of a rudimentary shelter made from palm fronds and sticks, near a freshwater stream in a jungle."},{"title":"Day 2 - Searching for Food","content":"My second day was focused on finding food. I managed to gather some wild berries and edible plants. I also spotted a few crabs near the shoreline. It's not a feast, but it's enough to keep me going.","imagePrompt":"Realistic first person image depicting the collection of wild berries and edible plants, with a few crabs spotted near a shoreline."},{"title":"Day 3 - Making a Fire","content":"I realized the importance of fire for warmth and cooking. After several attempts and using the friction method, I finally got a small fire going. It felt like a huge victory.","imagePrompt":"Realistic first person image showing the successful creation of a small fire in the jungle using the friction method."},{"title":"Day 4 - Exploring the Island","content":"Today, I explored more of the island. I found a variety of plants and a small cave. The cave could be a better shelter, but I need to inspect it more for safety.","imagePrompt":"Realistic first person image capturing the moment of discovering a variety of plants and a small cave on the island."},{"title":"Day 5 - Improving the Shelter","content":"I spent the day improving my shelter. I fortified the structure and made a makeshift bed from leaves and moss. It's more comfortable and secure now.","imagePrompt":"Realistic image showing an improved shelter fortified with more robust materials and a makeshift bed."},{"title":"Day 6 - Attempting to Fish","content":"I attempted fishing using a sharpened stick. It was challenging, and I wasn't successful, but I'm learning.","imagePrompt":"Realistic first person image of attempting to fish in a jungle stream with a handmade sharpened stick."},{"title":"Day 7 - Rest and Reflection","content":"I took this day to rest and reflect on my situation. I'm alone but surviving. It's a tough journey, but I'm adapting.","imagePrompt":"Realistic first person image capturing a moment of rest and reflection in the jungle environment."},{"title":"Day 8 - Crafting Tools","content":"I started crafting basic tools using stones and wood. A sharp stone became a rudimentary axe, which is quite useful.","imagePrompt":"Realistic first person image showing the crafting of basic tools from stones and wood in the jungle."},{"title":"Day 9 - Finding a Natural Spring","content":"I discovered a natural spring with cleaner water. It's a significant find for my survival.","imagePrompt":"Realistic first person image depicting the discovery of a natural spring with clear water in the jungle."},{"title":"Day 10 - Building a Raft","content":"I began constructing a small raft today. It's a long-term project, but it could be crucial for exploring or escape.","imagePrompt":"Realistic first person image showing the early stages of constructing a small raft near the island's shoreline."},{"title":"Day 11 - Fruit Harvest","content":"I found a grove of fruit trees today. I harvested as much as I could carry back. It's a welcome change from the berries and plants.","imagePrompt":"Realistic first person image capturing the harvest of various fruits from a grove of fruit trees on the island."},{"title":"Day 12 - Improving the Raft","content":"I dedicated more time to working on the raft. It's taking shape, and I'm learning to tie stronger knots and balance its structure.","imagePrompt":"Realistic first person image showing significant progress on the raft construction with stronger knots and balanced structure."},{"title":"Day 13 - First Rain","content":"It rained for the first time. I collected rainwater and worked on making my shelter more waterproof.","imagePrompt":"Realistic first person image showing the collection of rainwater and efforts to make the shelter waterproof during the first rain."},{"title":"Day 14 - Crafting a Spear","content":"I crafted a spear today. It's a more effective tool for fishing and might be useful for protection.","imagePrompt":"Realistic first person image depicting the crafting of a spear for fishing and protection in the jungle."},{"title":"Day 15 - Exploring the Cave","content":"I gathered the courage to explore the cave. It's larger than I thought, with some dry areas that could serve as a good shelter in emergencies.","imagePrompt":"Realistic first person image of exploring the inside of a large cave found on the island."},{"title":"Day 16 - Successful Fishing","content":"Using my new spear, I caught a fish today. It's a small victory, but it means a more substantial meal.","imagePrompt":"Realistic first person image capturing the moment of catching a fish with the newly crafted spear."},{"title":"Day 17 - Foraging for Medicinal Plants","content":"I foraged for plants that could have medicinal properties. It's important to be prepared for any health issues.","imagePrompt":"Realistic first person image showing the foraging and collection of various medicinal plants found in the jungle."},{"title":"Day 18 - Building a Signal Fire","content":"I built a large signal fire on a high point of the island. I plan to light it if I see any ships or planes.","imagePrompt":"Realistic first person image of a large signal fire built on a high point of the island."},{"title":"Day 19 - Exploring the Shoreline","content":"I walked the entire shoreline today. I found some interesting shells and another potential food source - clams.","imagePrompt":"Create a realistic first person image of walking along a deserted island's shoreline, exploring and collecting shells and clams."},{"title":"Day 20 - Improving My Living Space","content":"I improved my living space today, making it more comfortable and organized. It's important to maintain a good mental state.","imagePrompt":"Generate a realistic first person image showing an organized and comfortable living space in a jungle shelter, reflecting improvements for better living conditions."},{"title":"Day 21 - Observing Wildlife","content":"I spent the day observing the island's wildlife. Understanding their behavior can help me in foraging and staying safe.","imagePrompt":"Create a realistic first person image of quietly observing various wildlife on a deserted island, learning from their behaviors."},{"title":"Day 22 - Crafting a Bow and Arrow","content":"I began crafting a simple bow and arrow. It's challenging, but it could be a significant step in hunting.","imagePrompt":"Generate a realistic first person image showing the crafting of a simple bow and arrow in a jungle setting, symbolizing a step forward in hunting skills."},{"title":"Day 23 - Harvesting More Fruit","content":"I returned to the fruit grove to harvest more. This time, I tried to dry some of the fruit for preservation.","imagePrompt":"Create a realistic first person image of a person harvesting and drying fruit in a jungle, depicting efforts for food preservation."},{"title":"Day 24 - Raft Progress","content":"I made significant progress on the raft. It's nearly seaworthy, which gives me hope.","imagePrompt":"Generate a realistic first person image of a nearly completed raft near the shore of a deserted island, representing progress and hope for exploration or escape."},{"title":"Day 25 - Cooking Experiment","content":"I experimented with cooking different plants and fish. Some combinations are quite tasty.","imagePrompt":"Create a realistic first person image of cooking over a fire in a jungle setting, experimenting with different plant and fish combinations."},{"title":"Day 26 - Exploring Inland","content":"I ventured further inland today. The diversity of the flora and fauna is astounding.","imagePrompt":"Generate a realistic first person image of exploring the diverse inland flora and fauna of a deserted island, highlighting the journey’s exploration aspect."},{"title":"Day 27 - Building a Storage Pit","content":"I dug a storage pit for keeping food and tools safe and dry. It's a simple yet effective solution.","imagePrompt":"Create a realistic first person image showing the digging of a storage pit in a jungle setting for food and tool preservation."},{"title":"Day 28 - Star Gazing","content":"I spent the night star gazing. It's a reminder of the vastness of the world and my small place in it.","imagePrompt":"Generate a realistic first person image of a star gazing on a clear night on a deserted island, contemplating the vastness of the universe."},{"title":"Day 29 - Preparing for a Possible Journey","content":"I started preparing for a potential journey on the raft. I gathered supplies and mapped out a strategy.","imagePrompt":"Create a realistic first person image of preparing a raft with supplies on a deserted island's shore, planning for a possible journey."},{"title":"Day 30 - Reflection and Hope","content":"On my 30th day, I reflected on my journey. I've learned and adapted more than I ever thought possible. There's a sense of accomplishment, but also a lingering hope for rescue.","imagePrompt":"Generate a realistic first person image of reflecting beside a campfire on a deserted island, symbolizing the journey's end and the hope that remains."}] 2 | -------------------------------------------------------------------------------- /jungleBlog/src/components/Article.svelte: -------------------------------------------------------------------------------- 1 | 149 | 150 |
151 | {#if showContent} 152 |
153 |
154 |

{article.title}

155 |
156 | {article.title} 157 |
158 |

{article.content}

159 |
160 |
161 |
162 | 163 | 164 |
165 | 166 |
167 |
168 | {#if showComments} 169 |
170 | {#each comments as item} 171 |
172 |

{item.username}

173 |

{item.comment_text}

174 |
{formatLocalDateTime(item.created_at)}
175 |
176 | {/each} 177 |
178 | {/if} 179 | {/if} 180 |
181 | 182 | -------------------------------------------------------------------------------- /jungleBlog/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jungleblog", 3 | "version": "0.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "jungleblog", 9 | "version": "0.0.0", 10 | "devDependencies": { 11 | "@sveltejs/vite-plugin-svelte": "^2.4.2", 12 | "svelte": "^4.0.5", 13 | "vite": "^4.4.5" 14 | } 15 | }, 16 | "node_modules/@ampproject/remapping": { 17 | "version": "2.2.1", 18 | "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", 19 | "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", 20 | "dev": true, 21 | "dependencies": { 22 | "@jridgewell/gen-mapping": "^0.3.0", 23 | "@jridgewell/trace-mapping": "^0.3.9" 24 | }, 25 | "engines": { 26 | "node": ">=6.0.0" 27 | } 28 | }, 29 | "node_modules/@esbuild/android-arm": { 30 | "version": "0.18.20", 31 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", 32 | "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", 33 | "cpu": [ 34 | "arm" 35 | ], 36 | "dev": true, 37 | "optional": true, 38 | "os": [ 39 | "android" 40 | ], 41 | "engines": { 42 | "node": ">=12" 43 | } 44 | }, 45 | "node_modules/@esbuild/android-arm64": { 46 | "version": "0.18.20", 47 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", 48 | "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", 49 | "cpu": [ 50 | "arm64" 51 | ], 52 | "dev": true, 53 | "optional": true, 54 | "os": [ 55 | "android" 56 | ], 57 | "engines": { 58 | "node": ">=12" 59 | } 60 | }, 61 | "node_modules/@esbuild/android-x64": { 62 | "version": "0.18.20", 63 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", 64 | "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", 65 | "cpu": [ 66 | "x64" 67 | ], 68 | "dev": true, 69 | "optional": true, 70 | "os": [ 71 | "android" 72 | ], 73 | "engines": { 74 | "node": ">=12" 75 | } 76 | }, 77 | "node_modules/@esbuild/darwin-arm64": { 78 | "version": "0.18.20", 79 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", 80 | "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", 81 | "cpu": [ 82 | "arm64" 83 | ], 84 | "dev": true, 85 | "optional": true, 86 | "os": [ 87 | "darwin" 88 | ], 89 | "engines": { 90 | "node": ">=12" 91 | } 92 | }, 93 | "node_modules/@esbuild/darwin-x64": { 94 | "version": "0.18.20", 95 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", 96 | "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", 97 | "cpu": [ 98 | "x64" 99 | ], 100 | "dev": true, 101 | "optional": true, 102 | "os": [ 103 | "darwin" 104 | ], 105 | "engines": { 106 | "node": ">=12" 107 | } 108 | }, 109 | "node_modules/@esbuild/freebsd-arm64": { 110 | "version": "0.18.20", 111 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", 112 | "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", 113 | "cpu": [ 114 | "arm64" 115 | ], 116 | "dev": true, 117 | "optional": true, 118 | "os": [ 119 | "freebsd" 120 | ], 121 | "engines": { 122 | "node": ">=12" 123 | } 124 | }, 125 | "node_modules/@esbuild/freebsd-x64": { 126 | "version": "0.18.20", 127 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", 128 | "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", 129 | "cpu": [ 130 | "x64" 131 | ], 132 | "dev": true, 133 | "optional": true, 134 | "os": [ 135 | "freebsd" 136 | ], 137 | "engines": { 138 | "node": ">=12" 139 | } 140 | }, 141 | "node_modules/@esbuild/linux-arm": { 142 | "version": "0.18.20", 143 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", 144 | "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", 145 | "cpu": [ 146 | "arm" 147 | ], 148 | "dev": true, 149 | "optional": true, 150 | "os": [ 151 | "linux" 152 | ], 153 | "engines": { 154 | "node": ">=12" 155 | } 156 | }, 157 | "node_modules/@esbuild/linux-arm64": { 158 | "version": "0.18.20", 159 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", 160 | "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", 161 | "cpu": [ 162 | "arm64" 163 | ], 164 | "dev": true, 165 | "optional": true, 166 | "os": [ 167 | "linux" 168 | ], 169 | "engines": { 170 | "node": ">=12" 171 | } 172 | }, 173 | "node_modules/@esbuild/linux-ia32": { 174 | "version": "0.18.20", 175 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", 176 | "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", 177 | "cpu": [ 178 | "ia32" 179 | ], 180 | "dev": true, 181 | "optional": true, 182 | "os": [ 183 | "linux" 184 | ], 185 | "engines": { 186 | "node": ">=12" 187 | } 188 | }, 189 | "node_modules/@esbuild/linux-loong64": { 190 | "version": "0.18.20", 191 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", 192 | "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", 193 | "cpu": [ 194 | "loong64" 195 | ], 196 | "dev": true, 197 | "optional": true, 198 | "os": [ 199 | "linux" 200 | ], 201 | "engines": { 202 | "node": ">=12" 203 | } 204 | }, 205 | "node_modules/@esbuild/linux-mips64el": { 206 | "version": "0.18.20", 207 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", 208 | "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", 209 | "cpu": [ 210 | "mips64el" 211 | ], 212 | "dev": true, 213 | "optional": true, 214 | "os": [ 215 | "linux" 216 | ], 217 | "engines": { 218 | "node": ">=12" 219 | } 220 | }, 221 | "node_modules/@esbuild/linux-ppc64": { 222 | "version": "0.18.20", 223 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", 224 | "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", 225 | "cpu": [ 226 | "ppc64" 227 | ], 228 | "dev": true, 229 | "optional": true, 230 | "os": [ 231 | "linux" 232 | ], 233 | "engines": { 234 | "node": ">=12" 235 | } 236 | }, 237 | "node_modules/@esbuild/linux-riscv64": { 238 | "version": "0.18.20", 239 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", 240 | "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", 241 | "cpu": [ 242 | "riscv64" 243 | ], 244 | "dev": true, 245 | "optional": true, 246 | "os": [ 247 | "linux" 248 | ], 249 | "engines": { 250 | "node": ">=12" 251 | } 252 | }, 253 | "node_modules/@esbuild/linux-s390x": { 254 | "version": "0.18.20", 255 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", 256 | "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", 257 | "cpu": [ 258 | "s390x" 259 | ], 260 | "dev": true, 261 | "optional": true, 262 | "os": [ 263 | "linux" 264 | ], 265 | "engines": { 266 | "node": ">=12" 267 | } 268 | }, 269 | "node_modules/@esbuild/linux-x64": { 270 | "version": "0.18.20", 271 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", 272 | "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", 273 | "cpu": [ 274 | "x64" 275 | ], 276 | "dev": true, 277 | "optional": true, 278 | "os": [ 279 | "linux" 280 | ], 281 | "engines": { 282 | "node": ">=12" 283 | } 284 | }, 285 | "node_modules/@esbuild/netbsd-x64": { 286 | "version": "0.18.20", 287 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", 288 | "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", 289 | "cpu": [ 290 | "x64" 291 | ], 292 | "dev": true, 293 | "optional": true, 294 | "os": [ 295 | "netbsd" 296 | ], 297 | "engines": { 298 | "node": ">=12" 299 | } 300 | }, 301 | "node_modules/@esbuild/openbsd-x64": { 302 | "version": "0.18.20", 303 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", 304 | "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", 305 | "cpu": [ 306 | "x64" 307 | ], 308 | "dev": true, 309 | "optional": true, 310 | "os": [ 311 | "openbsd" 312 | ], 313 | "engines": { 314 | "node": ">=12" 315 | } 316 | }, 317 | "node_modules/@esbuild/sunos-x64": { 318 | "version": "0.18.20", 319 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", 320 | "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", 321 | "cpu": [ 322 | "x64" 323 | ], 324 | "dev": true, 325 | "optional": true, 326 | "os": [ 327 | "sunos" 328 | ], 329 | "engines": { 330 | "node": ">=12" 331 | } 332 | }, 333 | "node_modules/@esbuild/win32-arm64": { 334 | "version": "0.18.20", 335 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", 336 | "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", 337 | "cpu": [ 338 | "arm64" 339 | ], 340 | "dev": true, 341 | "optional": true, 342 | "os": [ 343 | "win32" 344 | ], 345 | "engines": { 346 | "node": ">=12" 347 | } 348 | }, 349 | "node_modules/@esbuild/win32-ia32": { 350 | "version": "0.18.20", 351 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", 352 | "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", 353 | "cpu": [ 354 | "ia32" 355 | ], 356 | "dev": true, 357 | "optional": true, 358 | "os": [ 359 | "win32" 360 | ], 361 | "engines": { 362 | "node": ">=12" 363 | } 364 | }, 365 | "node_modules/@esbuild/win32-x64": { 366 | "version": "0.18.20", 367 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", 368 | "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", 369 | "cpu": [ 370 | "x64" 371 | ], 372 | "dev": true, 373 | "optional": true, 374 | "os": [ 375 | "win32" 376 | ], 377 | "engines": { 378 | "node": ">=12" 379 | } 380 | }, 381 | "node_modules/@jridgewell/gen-mapping": { 382 | "version": "0.3.3", 383 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", 384 | "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", 385 | "dev": true, 386 | "dependencies": { 387 | "@jridgewell/set-array": "^1.0.1", 388 | "@jridgewell/sourcemap-codec": "^1.4.10", 389 | "@jridgewell/trace-mapping": "^0.3.9" 390 | }, 391 | "engines": { 392 | "node": ">=6.0.0" 393 | } 394 | }, 395 | "node_modules/@jridgewell/resolve-uri": { 396 | "version": "3.1.1", 397 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", 398 | "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", 399 | "dev": true, 400 | "engines": { 401 | "node": ">=6.0.0" 402 | } 403 | }, 404 | "node_modules/@jridgewell/set-array": { 405 | "version": "1.1.2", 406 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", 407 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", 408 | "dev": true, 409 | "engines": { 410 | "node": ">=6.0.0" 411 | } 412 | }, 413 | "node_modules/@jridgewell/sourcemap-codec": { 414 | "version": "1.4.15", 415 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 416 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 417 | "dev": true 418 | }, 419 | "node_modules/@jridgewell/trace-mapping": { 420 | "version": "0.3.20", 421 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", 422 | "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", 423 | "dev": true, 424 | "dependencies": { 425 | "@jridgewell/resolve-uri": "^3.1.0", 426 | "@jridgewell/sourcemap-codec": "^1.4.14" 427 | } 428 | }, 429 | "node_modules/@sveltejs/vite-plugin-svelte": { 430 | "version": "2.5.2", 431 | "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.5.2.tgz", 432 | "integrity": "sha512-Dfy0Rbl+IctOVfJvWGxrX/3m6vxPLH8o0x+8FA5QEyMUQMo4kGOVIojjryU7YomBAexOTAuYf1RT7809yDziaA==", 433 | "dev": true, 434 | "dependencies": { 435 | "@sveltejs/vite-plugin-svelte-inspector": "^1.0.4", 436 | "debug": "^4.3.4", 437 | "deepmerge": "^4.3.1", 438 | "kleur": "^4.1.5", 439 | "magic-string": "^0.30.3", 440 | "svelte-hmr": "^0.15.3", 441 | "vitefu": "^0.2.4" 442 | }, 443 | "engines": { 444 | "node": "^14.18.0 || >= 16" 445 | }, 446 | "peerDependencies": { 447 | "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0-next.0", 448 | "vite": "^4.0.0" 449 | } 450 | }, 451 | "node_modules/@sveltejs/vite-plugin-svelte-inspector": { 452 | "version": "1.0.4", 453 | "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-1.0.4.tgz", 454 | "integrity": "sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==", 455 | "dev": true, 456 | "dependencies": { 457 | "debug": "^4.3.4" 458 | }, 459 | "engines": { 460 | "node": "^14.18.0 || >= 16" 461 | }, 462 | "peerDependencies": { 463 | "@sveltejs/vite-plugin-svelte": "^2.2.0", 464 | "svelte": "^3.54.0 || ^4.0.0", 465 | "vite": "^4.0.0" 466 | } 467 | }, 468 | "node_modules/@types/estree": { 469 | "version": "1.0.5", 470 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", 471 | "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", 472 | "dev": true 473 | }, 474 | "node_modules/acorn": { 475 | "version": "8.11.2", 476 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", 477 | "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", 478 | "dev": true, 479 | "bin": { 480 | "acorn": "bin/acorn" 481 | }, 482 | "engines": { 483 | "node": ">=0.4.0" 484 | } 485 | }, 486 | "node_modules/aria-query": { 487 | "version": "5.3.0", 488 | "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", 489 | "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", 490 | "dev": true, 491 | "dependencies": { 492 | "dequal": "^2.0.3" 493 | } 494 | }, 495 | "node_modules/axobject-query": { 496 | "version": "3.2.1", 497 | "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", 498 | "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", 499 | "dev": true, 500 | "dependencies": { 501 | "dequal": "^2.0.3" 502 | } 503 | }, 504 | "node_modules/code-red": { 505 | "version": "1.0.4", 506 | "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", 507 | "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", 508 | "dev": true, 509 | "dependencies": { 510 | "@jridgewell/sourcemap-codec": "^1.4.15", 511 | "@types/estree": "^1.0.1", 512 | "acorn": "^8.10.0", 513 | "estree-walker": "^3.0.3", 514 | "periscopic": "^3.1.0" 515 | } 516 | }, 517 | "node_modules/css-tree": { 518 | "version": "2.3.1", 519 | "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", 520 | "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", 521 | "dev": true, 522 | "dependencies": { 523 | "mdn-data": "2.0.30", 524 | "source-map-js": "^1.0.1" 525 | }, 526 | "engines": { 527 | "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" 528 | } 529 | }, 530 | "node_modules/debug": { 531 | "version": "4.3.4", 532 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 533 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 534 | "dev": true, 535 | "dependencies": { 536 | "ms": "2.1.2" 537 | }, 538 | "engines": { 539 | "node": ">=6.0" 540 | }, 541 | "peerDependenciesMeta": { 542 | "supports-color": { 543 | "optional": true 544 | } 545 | } 546 | }, 547 | "node_modules/deepmerge": { 548 | "version": "4.3.1", 549 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", 550 | "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", 551 | "dev": true, 552 | "engines": { 553 | "node": ">=0.10.0" 554 | } 555 | }, 556 | "node_modules/dequal": { 557 | "version": "2.0.3", 558 | "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", 559 | "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", 560 | "dev": true, 561 | "engines": { 562 | "node": ">=6" 563 | } 564 | }, 565 | "node_modules/esbuild": { 566 | "version": "0.18.20", 567 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", 568 | "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", 569 | "dev": true, 570 | "hasInstallScript": true, 571 | "bin": { 572 | "esbuild": "bin/esbuild" 573 | }, 574 | "engines": { 575 | "node": ">=12" 576 | }, 577 | "optionalDependencies": { 578 | "@esbuild/android-arm": "0.18.20", 579 | "@esbuild/android-arm64": "0.18.20", 580 | "@esbuild/android-x64": "0.18.20", 581 | "@esbuild/darwin-arm64": "0.18.20", 582 | "@esbuild/darwin-x64": "0.18.20", 583 | "@esbuild/freebsd-arm64": "0.18.20", 584 | "@esbuild/freebsd-x64": "0.18.20", 585 | "@esbuild/linux-arm": "0.18.20", 586 | "@esbuild/linux-arm64": "0.18.20", 587 | "@esbuild/linux-ia32": "0.18.20", 588 | "@esbuild/linux-loong64": "0.18.20", 589 | "@esbuild/linux-mips64el": "0.18.20", 590 | "@esbuild/linux-ppc64": "0.18.20", 591 | "@esbuild/linux-riscv64": "0.18.20", 592 | "@esbuild/linux-s390x": "0.18.20", 593 | "@esbuild/linux-x64": "0.18.20", 594 | "@esbuild/netbsd-x64": "0.18.20", 595 | "@esbuild/openbsd-x64": "0.18.20", 596 | "@esbuild/sunos-x64": "0.18.20", 597 | "@esbuild/win32-arm64": "0.18.20", 598 | "@esbuild/win32-ia32": "0.18.20", 599 | "@esbuild/win32-x64": "0.18.20" 600 | } 601 | }, 602 | "node_modules/estree-walker": { 603 | "version": "3.0.3", 604 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", 605 | "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", 606 | "dev": true, 607 | "dependencies": { 608 | "@types/estree": "^1.0.0" 609 | } 610 | }, 611 | "node_modules/fsevents": { 612 | "version": "2.3.3", 613 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 614 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 615 | "dev": true, 616 | "hasInstallScript": true, 617 | "optional": true, 618 | "os": [ 619 | "darwin" 620 | ], 621 | "engines": { 622 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 623 | } 624 | }, 625 | "node_modules/is-reference": { 626 | "version": "3.0.2", 627 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", 628 | "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", 629 | "dev": true, 630 | "dependencies": { 631 | "@types/estree": "*" 632 | } 633 | }, 634 | "node_modules/kleur": { 635 | "version": "4.1.5", 636 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", 637 | "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", 638 | "dev": true, 639 | "engines": { 640 | "node": ">=6" 641 | } 642 | }, 643 | "node_modules/locate-character": { 644 | "version": "3.0.0", 645 | "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", 646 | "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", 647 | "dev": true 648 | }, 649 | "node_modules/magic-string": { 650 | "version": "0.30.5", 651 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", 652 | "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", 653 | "dev": true, 654 | "dependencies": { 655 | "@jridgewell/sourcemap-codec": "^1.4.15" 656 | }, 657 | "engines": { 658 | "node": ">=12" 659 | } 660 | }, 661 | "node_modules/mdn-data": { 662 | "version": "2.0.30", 663 | "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", 664 | "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", 665 | "dev": true 666 | }, 667 | "node_modules/ms": { 668 | "version": "2.1.2", 669 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 670 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 671 | "dev": true 672 | }, 673 | "node_modules/nanoid": { 674 | "version": "3.3.7", 675 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", 676 | "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", 677 | "dev": true, 678 | "funding": [ 679 | { 680 | "type": "github", 681 | "url": "https://github.com/sponsors/ai" 682 | } 683 | ], 684 | "bin": { 685 | "nanoid": "bin/nanoid.cjs" 686 | }, 687 | "engines": { 688 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 689 | } 690 | }, 691 | "node_modules/periscopic": { 692 | "version": "3.1.0", 693 | "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", 694 | "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", 695 | "dev": true, 696 | "dependencies": { 697 | "@types/estree": "^1.0.0", 698 | "estree-walker": "^3.0.0", 699 | "is-reference": "^3.0.0" 700 | } 701 | }, 702 | "node_modules/picocolors": { 703 | "version": "1.0.0", 704 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 705 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 706 | "dev": true 707 | }, 708 | "node_modules/postcss": { 709 | "version": "8.4.31", 710 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", 711 | "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", 712 | "dev": true, 713 | "funding": [ 714 | { 715 | "type": "opencollective", 716 | "url": "https://opencollective.com/postcss/" 717 | }, 718 | { 719 | "type": "tidelift", 720 | "url": "https://tidelift.com/funding/github/npm/postcss" 721 | }, 722 | { 723 | "type": "github", 724 | "url": "https://github.com/sponsors/ai" 725 | } 726 | ], 727 | "dependencies": { 728 | "nanoid": "^3.3.6", 729 | "picocolors": "^1.0.0", 730 | "source-map-js": "^1.0.2" 731 | }, 732 | "engines": { 733 | "node": "^10 || ^12 || >=14" 734 | } 735 | }, 736 | "node_modules/rollup": { 737 | "version": "3.29.4", 738 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", 739 | "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", 740 | "dev": true, 741 | "bin": { 742 | "rollup": "dist/bin/rollup" 743 | }, 744 | "engines": { 745 | "node": ">=14.18.0", 746 | "npm": ">=8.0.0" 747 | }, 748 | "optionalDependencies": { 749 | "fsevents": "~2.3.2" 750 | } 751 | }, 752 | "node_modules/source-map-js": { 753 | "version": "1.0.2", 754 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 755 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 756 | "dev": true, 757 | "engines": { 758 | "node": ">=0.10.0" 759 | } 760 | }, 761 | "node_modules/svelte": { 762 | "version": "4.2.3", 763 | "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.3.tgz", 764 | "integrity": "sha512-sqmG9KC6uUc7fb3ZuWoxXvqk6MI9Uu4ABA1M0fYDgTlFYu1k02xp96u6U9+yJZiVm84m9zge7rrA/BNZdFpOKw==", 765 | "dev": true, 766 | "dependencies": { 767 | "@ampproject/remapping": "^2.2.1", 768 | "@jridgewell/sourcemap-codec": "^1.4.15", 769 | "@jridgewell/trace-mapping": "^0.3.18", 770 | "acorn": "^8.9.0", 771 | "aria-query": "^5.3.0", 772 | "axobject-query": "^3.2.1", 773 | "code-red": "^1.0.3", 774 | "css-tree": "^2.3.1", 775 | "estree-walker": "^3.0.3", 776 | "is-reference": "^3.0.1", 777 | "locate-character": "^3.0.0", 778 | "magic-string": "^0.30.4", 779 | "periscopic": "^3.1.0" 780 | }, 781 | "engines": { 782 | "node": ">=16" 783 | } 784 | }, 785 | "node_modules/svelte-hmr": { 786 | "version": "0.15.3", 787 | "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz", 788 | "integrity": "sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==", 789 | "dev": true, 790 | "engines": { 791 | "node": "^12.20 || ^14.13.1 || >= 16" 792 | }, 793 | "peerDependencies": { 794 | "svelte": "^3.19.0 || ^4.0.0" 795 | } 796 | }, 797 | "node_modules/vite": { 798 | "version": "4.5.0", 799 | "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", 800 | "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", 801 | "dev": true, 802 | "dependencies": { 803 | "esbuild": "^0.18.10", 804 | "postcss": "^8.4.27", 805 | "rollup": "^3.27.1" 806 | }, 807 | "bin": { 808 | "vite": "bin/vite.js" 809 | }, 810 | "engines": { 811 | "node": "^14.18.0 || >=16.0.0" 812 | }, 813 | "funding": { 814 | "url": "https://github.com/vitejs/vite?sponsor=1" 815 | }, 816 | "optionalDependencies": { 817 | "fsevents": "~2.3.2" 818 | }, 819 | "peerDependencies": { 820 | "@types/node": ">= 14", 821 | "less": "*", 822 | "lightningcss": "^1.21.0", 823 | "sass": "*", 824 | "stylus": "*", 825 | "sugarss": "*", 826 | "terser": "^5.4.0" 827 | }, 828 | "peerDependenciesMeta": { 829 | "@types/node": { 830 | "optional": true 831 | }, 832 | "less": { 833 | "optional": true 834 | }, 835 | "lightningcss": { 836 | "optional": true 837 | }, 838 | "sass": { 839 | "optional": true 840 | }, 841 | "stylus": { 842 | "optional": true 843 | }, 844 | "sugarss": { 845 | "optional": true 846 | }, 847 | "terser": { 848 | "optional": true 849 | } 850 | } 851 | }, 852 | "node_modules/vitefu": { 853 | "version": "0.2.5", 854 | "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", 855 | "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", 856 | "dev": true, 857 | "peerDependencies": { 858 | "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" 859 | }, 860 | "peerDependenciesMeta": { 861 | "vite": { 862 | "optional": true 863 | } 864 | } 865 | } 866 | } 867 | } 868 | -------------------------------------------------------------------------------- /adminPanel/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adminpanel", 3 | "version": "0.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "adminpanel", 9 | "version": "0.0.0", 10 | "devDependencies": { 11 | "@sveltejs/vite-plugin-svelte": "^3.0.1", 12 | "svelte": "^4.2.8", 13 | "vite": "^5.0.8" 14 | } 15 | }, 16 | "node_modules/@ampproject/remapping": { 17 | "version": "2.2.1", 18 | "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", 19 | "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", 20 | "dev": true, 21 | "dependencies": { 22 | "@jridgewell/gen-mapping": "^0.3.0", 23 | "@jridgewell/trace-mapping": "^0.3.9" 24 | }, 25 | "engines": { 26 | "node": ">=6.0.0" 27 | } 28 | }, 29 | "node_modules/@esbuild/aix-ppc64": { 30 | "version": "0.19.11", 31 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz", 32 | "integrity": "sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==", 33 | "cpu": [ 34 | "ppc64" 35 | ], 36 | "dev": true, 37 | "optional": true, 38 | "os": [ 39 | "aix" 40 | ], 41 | "engines": { 42 | "node": ">=12" 43 | } 44 | }, 45 | "node_modules/@esbuild/android-arm": { 46 | "version": "0.19.11", 47 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.11.tgz", 48 | "integrity": "sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==", 49 | "cpu": [ 50 | "arm" 51 | ], 52 | "dev": true, 53 | "optional": true, 54 | "os": [ 55 | "android" 56 | ], 57 | "engines": { 58 | "node": ">=12" 59 | } 60 | }, 61 | "node_modules/@esbuild/android-arm64": { 62 | "version": "0.19.11", 63 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz", 64 | "integrity": "sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==", 65 | "cpu": [ 66 | "arm64" 67 | ], 68 | "dev": true, 69 | "optional": true, 70 | "os": [ 71 | "android" 72 | ], 73 | "engines": { 74 | "node": ">=12" 75 | } 76 | }, 77 | "node_modules/@esbuild/android-x64": { 78 | "version": "0.19.11", 79 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.11.tgz", 80 | "integrity": "sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==", 81 | "cpu": [ 82 | "x64" 83 | ], 84 | "dev": true, 85 | "optional": true, 86 | "os": [ 87 | "android" 88 | ], 89 | "engines": { 90 | "node": ">=12" 91 | } 92 | }, 93 | "node_modules/@esbuild/darwin-arm64": { 94 | "version": "0.19.11", 95 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz", 96 | "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==", 97 | "cpu": [ 98 | "arm64" 99 | ], 100 | "dev": true, 101 | "optional": true, 102 | "os": [ 103 | "darwin" 104 | ], 105 | "engines": { 106 | "node": ">=12" 107 | } 108 | }, 109 | "node_modules/@esbuild/darwin-x64": { 110 | "version": "0.19.11", 111 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz", 112 | "integrity": "sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==", 113 | "cpu": [ 114 | "x64" 115 | ], 116 | "dev": true, 117 | "optional": true, 118 | "os": [ 119 | "darwin" 120 | ], 121 | "engines": { 122 | "node": ">=12" 123 | } 124 | }, 125 | "node_modules/@esbuild/freebsd-arm64": { 126 | "version": "0.19.11", 127 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz", 128 | "integrity": "sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==", 129 | "cpu": [ 130 | "arm64" 131 | ], 132 | "dev": true, 133 | "optional": true, 134 | "os": [ 135 | "freebsd" 136 | ], 137 | "engines": { 138 | "node": ">=12" 139 | } 140 | }, 141 | "node_modules/@esbuild/freebsd-x64": { 142 | "version": "0.19.11", 143 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz", 144 | "integrity": "sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==", 145 | "cpu": [ 146 | "x64" 147 | ], 148 | "dev": true, 149 | "optional": true, 150 | "os": [ 151 | "freebsd" 152 | ], 153 | "engines": { 154 | "node": ">=12" 155 | } 156 | }, 157 | "node_modules/@esbuild/linux-arm": { 158 | "version": "0.19.11", 159 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz", 160 | "integrity": "sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==", 161 | "cpu": [ 162 | "arm" 163 | ], 164 | "dev": true, 165 | "optional": true, 166 | "os": [ 167 | "linux" 168 | ], 169 | "engines": { 170 | "node": ">=12" 171 | } 172 | }, 173 | "node_modules/@esbuild/linux-arm64": { 174 | "version": "0.19.11", 175 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz", 176 | "integrity": "sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==", 177 | "cpu": [ 178 | "arm64" 179 | ], 180 | "dev": true, 181 | "optional": true, 182 | "os": [ 183 | "linux" 184 | ], 185 | "engines": { 186 | "node": ">=12" 187 | } 188 | }, 189 | "node_modules/@esbuild/linux-ia32": { 190 | "version": "0.19.11", 191 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz", 192 | "integrity": "sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==", 193 | "cpu": [ 194 | "ia32" 195 | ], 196 | "dev": true, 197 | "optional": true, 198 | "os": [ 199 | "linux" 200 | ], 201 | "engines": { 202 | "node": ">=12" 203 | } 204 | }, 205 | "node_modules/@esbuild/linux-loong64": { 206 | "version": "0.19.11", 207 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz", 208 | "integrity": "sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==", 209 | "cpu": [ 210 | "loong64" 211 | ], 212 | "dev": true, 213 | "optional": true, 214 | "os": [ 215 | "linux" 216 | ], 217 | "engines": { 218 | "node": ">=12" 219 | } 220 | }, 221 | "node_modules/@esbuild/linux-mips64el": { 222 | "version": "0.19.11", 223 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz", 224 | "integrity": "sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==", 225 | "cpu": [ 226 | "mips64el" 227 | ], 228 | "dev": true, 229 | "optional": true, 230 | "os": [ 231 | "linux" 232 | ], 233 | "engines": { 234 | "node": ">=12" 235 | } 236 | }, 237 | "node_modules/@esbuild/linux-ppc64": { 238 | "version": "0.19.11", 239 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz", 240 | "integrity": "sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==", 241 | "cpu": [ 242 | "ppc64" 243 | ], 244 | "dev": true, 245 | "optional": true, 246 | "os": [ 247 | "linux" 248 | ], 249 | "engines": { 250 | "node": ">=12" 251 | } 252 | }, 253 | "node_modules/@esbuild/linux-riscv64": { 254 | "version": "0.19.11", 255 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz", 256 | "integrity": "sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==", 257 | "cpu": [ 258 | "riscv64" 259 | ], 260 | "dev": true, 261 | "optional": true, 262 | "os": [ 263 | "linux" 264 | ], 265 | "engines": { 266 | "node": ">=12" 267 | } 268 | }, 269 | "node_modules/@esbuild/linux-s390x": { 270 | "version": "0.19.11", 271 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz", 272 | "integrity": "sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==", 273 | "cpu": [ 274 | "s390x" 275 | ], 276 | "dev": true, 277 | "optional": true, 278 | "os": [ 279 | "linux" 280 | ], 281 | "engines": { 282 | "node": ">=12" 283 | } 284 | }, 285 | "node_modules/@esbuild/linux-x64": { 286 | "version": "0.19.11", 287 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz", 288 | "integrity": "sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==", 289 | "cpu": [ 290 | "x64" 291 | ], 292 | "dev": true, 293 | "optional": true, 294 | "os": [ 295 | "linux" 296 | ], 297 | "engines": { 298 | "node": ">=12" 299 | } 300 | }, 301 | "node_modules/@esbuild/netbsd-x64": { 302 | "version": "0.19.11", 303 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz", 304 | "integrity": "sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==", 305 | "cpu": [ 306 | "x64" 307 | ], 308 | "dev": true, 309 | "optional": true, 310 | "os": [ 311 | "netbsd" 312 | ], 313 | "engines": { 314 | "node": ">=12" 315 | } 316 | }, 317 | "node_modules/@esbuild/openbsd-x64": { 318 | "version": "0.19.11", 319 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz", 320 | "integrity": "sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==", 321 | "cpu": [ 322 | "x64" 323 | ], 324 | "dev": true, 325 | "optional": true, 326 | "os": [ 327 | "openbsd" 328 | ], 329 | "engines": { 330 | "node": ">=12" 331 | } 332 | }, 333 | "node_modules/@esbuild/sunos-x64": { 334 | "version": "0.19.11", 335 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz", 336 | "integrity": "sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==", 337 | "cpu": [ 338 | "x64" 339 | ], 340 | "dev": true, 341 | "optional": true, 342 | "os": [ 343 | "sunos" 344 | ], 345 | "engines": { 346 | "node": ">=12" 347 | } 348 | }, 349 | "node_modules/@esbuild/win32-arm64": { 350 | "version": "0.19.11", 351 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz", 352 | "integrity": "sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==", 353 | "cpu": [ 354 | "arm64" 355 | ], 356 | "dev": true, 357 | "optional": true, 358 | "os": [ 359 | "win32" 360 | ], 361 | "engines": { 362 | "node": ">=12" 363 | } 364 | }, 365 | "node_modules/@esbuild/win32-ia32": { 366 | "version": "0.19.11", 367 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz", 368 | "integrity": "sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==", 369 | "cpu": [ 370 | "ia32" 371 | ], 372 | "dev": true, 373 | "optional": true, 374 | "os": [ 375 | "win32" 376 | ], 377 | "engines": { 378 | "node": ">=12" 379 | } 380 | }, 381 | "node_modules/@esbuild/win32-x64": { 382 | "version": "0.19.11", 383 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz", 384 | "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==", 385 | "cpu": [ 386 | "x64" 387 | ], 388 | "dev": true, 389 | "optional": true, 390 | "os": [ 391 | "win32" 392 | ], 393 | "engines": { 394 | "node": ">=12" 395 | } 396 | }, 397 | "node_modules/@jridgewell/gen-mapping": { 398 | "version": "0.3.3", 399 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", 400 | "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", 401 | "dev": true, 402 | "dependencies": { 403 | "@jridgewell/set-array": "^1.0.1", 404 | "@jridgewell/sourcemap-codec": "^1.4.10", 405 | "@jridgewell/trace-mapping": "^0.3.9" 406 | }, 407 | "engines": { 408 | "node": ">=6.0.0" 409 | } 410 | }, 411 | "node_modules/@jridgewell/resolve-uri": { 412 | "version": "3.1.1", 413 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", 414 | "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", 415 | "dev": true, 416 | "engines": { 417 | "node": ">=6.0.0" 418 | } 419 | }, 420 | "node_modules/@jridgewell/set-array": { 421 | "version": "1.1.2", 422 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", 423 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", 424 | "dev": true, 425 | "engines": { 426 | "node": ">=6.0.0" 427 | } 428 | }, 429 | "node_modules/@jridgewell/sourcemap-codec": { 430 | "version": "1.4.15", 431 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 432 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 433 | "dev": true 434 | }, 435 | "node_modules/@jridgewell/trace-mapping": { 436 | "version": "0.3.20", 437 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", 438 | "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", 439 | "dev": true, 440 | "dependencies": { 441 | "@jridgewell/resolve-uri": "^3.1.0", 442 | "@jridgewell/sourcemap-codec": "^1.4.14" 443 | } 444 | }, 445 | "node_modules/@rollup/rollup-android-arm-eabi": { 446 | "version": "4.9.4", 447 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.4.tgz", 448 | "integrity": "sha512-ub/SN3yWqIv5CWiAZPHVS1DloyZsJbtXmX4HxUTIpS0BHm9pW5iYBo2mIZi+hE3AeiTzHz33blwSnhdUo+9NpA==", 449 | "cpu": [ 450 | "arm" 451 | ], 452 | "dev": true, 453 | "optional": true, 454 | "os": [ 455 | "android" 456 | ] 457 | }, 458 | "node_modules/@rollup/rollup-android-arm64": { 459 | "version": "4.9.4", 460 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.4.tgz", 461 | "integrity": "sha512-ehcBrOR5XTl0W0t2WxfTyHCR/3Cq2jfb+I4W+Ch8Y9b5G+vbAecVv0Fx/J1QKktOrgUYsIKxWAKgIpvw56IFNA==", 462 | "cpu": [ 463 | "arm64" 464 | ], 465 | "dev": true, 466 | "optional": true, 467 | "os": [ 468 | "android" 469 | ] 470 | }, 471 | "node_modules/@rollup/rollup-darwin-arm64": { 472 | "version": "4.9.4", 473 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.4.tgz", 474 | "integrity": "sha512-1fzh1lWExwSTWy8vJPnNbNM02WZDS8AW3McEOb7wW+nPChLKf3WG2aG7fhaUmfX5FKw9zhsF5+MBwArGyNM7NA==", 475 | "cpu": [ 476 | "arm64" 477 | ], 478 | "dev": true, 479 | "optional": true, 480 | "os": [ 481 | "darwin" 482 | ] 483 | }, 484 | "node_modules/@rollup/rollup-darwin-x64": { 485 | "version": "4.9.4", 486 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.4.tgz", 487 | "integrity": "sha512-Gc6cukkF38RcYQ6uPdiXi70JB0f29CwcQ7+r4QpfNpQFVHXRd0DfWFidoGxjSx1DwOETM97JPz1RXL5ISSB0pA==", 488 | "cpu": [ 489 | "x64" 490 | ], 491 | "dev": true, 492 | "optional": true, 493 | "os": [ 494 | "darwin" 495 | ] 496 | }, 497 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 498 | "version": "4.9.4", 499 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.4.tgz", 500 | "integrity": "sha512-g21RTeFzoTl8GxosHbnQZ0/JkuFIB13C3T7Y0HtKzOXmoHhewLbVTFBQZu+z5m9STH6FZ7L/oPgU4Nm5ErN2fw==", 501 | "cpu": [ 502 | "arm" 503 | ], 504 | "dev": true, 505 | "optional": true, 506 | "os": [ 507 | "linux" 508 | ] 509 | }, 510 | "node_modules/@rollup/rollup-linux-arm64-gnu": { 511 | "version": "4.9.4", 512 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.4.tgz", 513 | "integrity": "sha512-TVYVWD/SYwWzGGnbfTkrNpdE4HON46orgMNHCivlXmlsSGQOx/OHHYiQcMIOx38/GWgwr/po2LBn7wypkWw/Mg==", 514 | "cpu": [ 515 | "arm64" 516 | ], 517 | "dev": true, 518 | "optional": true, 519 | "os": [ 520 | "linux" 521 | ] 522 | }, 523 | "node_modules/@rollup/rollup-linux-arm64-musl": { 524 | "version": "4.9.4", 525 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.4.tgz", 526 | "integrity": "sha512-XcKvuendwizYYhFxpvQ3xVpzje2HHImzg33wL9zvxtj77HvPStbSGI9czrdbfrf8DGMcNNReH9pVZv8qejAQ5A==", 527 | "cpu": [ 528 | "arm64" 529 | ], 530 | "dev": true, 531 | "optional": true, 532 | "os": [ 533 | "linux" 534 | ] 535 | }, 536 | "node_modules/@rollup/rollup-linux-riscv64-gnu": { 537 | "version": "4.9.4", 538 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.4.tgz", 539 | "integrity": "sha512-LFHS/8Q+I9YA0yVETyjonMJ3UA+DczeBd/MqNEzsGSTdNvSJa1OJZcSH8GiXLvcizgp9AlHs2walqRcqzjOi3A==", 540 | "cpu": [ 541 | "riscv64" 542 | ], 543 | "dev": true, 544 | "optional": true, 545 | "os": [ 546 | "linux" 547 | ] 548 | }, 549 | "node_modules/@rollup/rollup-linux-x64-gnu": { 550 | "version": "4.9.4", 551 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.4.tgz", 552 | "integrity": "sha512-dIYgo+j1+yfy81i0YVU5KnQrIJZE8ERomx17ReU4GREjGtDW4X+nvkBak2xAUpyqLs4eleDSj3RrV72fQos7zw==", 553 | "cpu": [ 554 | "x64" 555 | ], 556 | "dev": true, 557 | "optional": true, 558 | "os": [ 559 | "linux" 560 | ] 561 | }, 562 | "node_modules/@rollup/rollup-linux-x64-musl": { 563 | "version": "4.9.4", 564 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.4.tgz", 565 | "integrity": "sha512-RoaYxjdHQ5TPjaPrLsfKqR3pakMr3JGqZ+jZM0zP2IkDtsGa4CqYaWSfQmZVgFUCgLrTnzX+cnHS3nfl+kB6ZQ==", 566 | "cpu": [ 567 | "x64" 568 | ], 569 | "dev": true, 570 | "optional": true, 571 | "os": [ 572 | "linux" 573 | ] 574 | }, 575 | "node_modules/@rollup/rollup-win32-arm64-msvc": { 576 | "version": "4.9.4", 577 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.4.tgz", 578 | "integrity": "sha512-T8Q3XHV+Jjf5e49B4EAaLKV74BbX7/qYBRQ8Wop/+TyyU0k+vSjiLVSHNWdVd1goMjZcbhDmYZUYW5RFqkBNHQ==", 579 | "cpu": [ 580 | "arm64" 581 | ], 582 | "dev": true, 583 | "optional": true, 584 | "os": [ 585 | "win32" 586 | ] 587 | }, 588 | "node_modules/@rollup/rollup-win32-ia32-msvc": { 589 | "version": "4.9.4", 590 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.4.tgz", 591 | "integrity": "sha512-z+JQ7JirDUHAsMecVydnBPWLwJjbppU+7LZjffGf+Jvrxq+dVjIE7By163Sc9DKc3ADSU50qPVw0KonBS+a+HQ==", 592 | "cpu": [ 593 | "ia32" 594 | ], 595 | "dev": true, 596 | "optional": true, 597 | "os": [ 598 | "win32" 599 | ] 600 | }, 601 | "node_modules/@rollup/rollup-win32-x64-msvc": { 602 | "version": "4.9.4", 603 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.4.tgz", 604 | "integrity": "sha512-LfdGXCV9rdEify1oxlN9eamvDSjv9md9ZVMAbNHA87xqIfFCxImxan9qZ8+Un54iK2nnqPlbnSi4R54ONtbWBw==", 605 | "cpu": [ 606 | "x64" 607 | ], 608 | "dev": true, 609 | "optional": true, 610 | "os": [ 611 | "win32" 612 | ] 613 | }, 614 | "node_modules/@sveltejs/vite-plugin-svelte": { 615 | "version": "3.0.1", 616 | "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.0.1.tgz", 617 | "integrity": "sha512-CGURX6Ps+TkOovK6xV+Y2rn8JKa8ZPUHPZ/NKgCxAmgBrXReavzFl8aOSCj3kQ1xqT7yGJj53hjcV/gqwDAaWA==", 618 | "dev": true, 619 | "dependencies": { 620 | "@sveltejs/vite-plugin-svelte-inspector": "^2.0.0-next.0 || ^2.0.0", 621 | "debug": "^4.3.4", 622 | "deepmerge": "^4.3.1", 623 | "kleur": "^4.1.5", 624 | "magic-string": "^0.30.5", 625 | "svelte-hmr": "^0.15.3", 626 | "vitefu": "^0.2.5" 627 | }, 628 | "engines": { 629 | "node": "^18.0.0 || >=20" 630 | }, 631 | "peerDependencies": { 632 | "svelte": "^4.0.0 || ^5.0.0-next.0", 633 | "vite": "^5.0.0" 634 | } 635 | }, 636 | "node_modules/@sveltejs/vite-plugin-svelte-inspector": { 637 | "version": "2.0.0", 638 | "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.0.0.tgz", 639 | "integrity": "sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==", 640 | "dev": true, 641 | "dependencies": { 642 | "debug": "^4.3.4" 643 | }, 644 | "engines": { 645 | "node": "^18.0.0 || >=20" 646 | }, 647 | "peerDependencies": { 648 | "@sveltejs/vite-plugin-svelte": "^3.0.0", 649 | "svelte": "^4.0.0 || ^5.0.0-next.0", 650 | "vite": "^5.0.0" 651 | } 652 | }, 653 | "node_modules/@types/estree": { 654 | "version": "1.0.5", 655 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", 656 | "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", 657 | "dev": true 658 | }, 659 | "node_modules/acorn": { 660 | "version": "8.11.3", 661 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", 662 | "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", 663 | "dev": true, 664 | "bin": { 665 | "acorn": "bin/acorn" 666 | }, 667 | "engines": { 668 | "node": ">=0.4.0" 669 | } 670 | }, 671 | "node_modules/aria-query": { 672 | "version": "5.3.0", 673 | "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", 674 | "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", 675 | "dev": true, 676 | "dependencies": { 677 | "dequal": "^2.0.3" 678 | } 679 | }, 680 | "node_modules/axobject-query": { 681 | "version": "3.2.1", 682 | "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", 683 | "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", 684 | "dev": true, 685 | "dependencies": { 686 | "dequal": "^2.0.3" 687 | } 688 | }, 689 | "node_modules/code-red": { 690 | "version": "1.0.4", 691 | "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", 692 | "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", 693 | "dev": true, 694 | "dependencies": { 695 | "@jridgewell/sourcemap-codec": "^1.4.15", 696 | "@types/estree": "^1.0.1", 697 | "acorn": "^8.10.0", 698 | "estree-walker": "^3.0.3", 699 | "periscopic": "^3.1.0" 700 | } 701 | }, 702 | "node_modules/css-tree": { 703 | "version": "2.3.1", 704 | "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", 705 | "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", 706 | "dev": true, 707 | "dependencies": { 708 | "mdn-data": "2.0.30", 709 | "source-map-js": "^1.0.1" 710 | }, 711 | "engines": { 712 | "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" 713 | } 714 | }, 715 | "node_modules/debug": { 716 | "version": "4.3.4", 717 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 718 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 719 | "dev": true, 720 | "dependencies": { 721 | "ms": "2.1.2" 722 | }, 723 | "engines": { 724 | "node": ">=6.0" 725 | }, 726 | "peerDependenciesMeta": { 727 | "supports-color": { 728 | "optional": true 729 | } 730 | } 731 | }, 732 | "node_modules/deepmerge": { 733 | "version": "4.3.1", 734 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", 735 | "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", 736 | "dev": true, 737 | "engines": { 738 | "node": ">=0.10.0" 739 | } 740 | }, 741 | "node_modules/dequal": { 742 | "version": "2.0.3", 743 | "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", 744 | "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", 745 | "dev": true, 746 | "engines": { 747 | "node": ">=6" 748 | } 749 | }, 750 | "node_modules/esbuild": { 751 | "version": "0.19.11", 752 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz", 753 | "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==", 754 | "dev": true, 755 | "hasInstallScript": true, 756 | "bin": { 757 | "esbuild": "bin/esbuild" 758 | }, 759 | "engines": { 760 | "node": ">=12" 761 | }, 762 | "optionalDependencies": { 763 | "@esbuild/aix-ppc64": "0.19.11", 764 | "@esbuild/android-arm": "0.19.11", 765 | "@esbuild/android-arm64": "0.19.11", 766 | "@esbuild/android-x64": "0.19.11", 767 | "@esbuild/darwin-arm64": "0.19.11", 768 | "@esbuild/darwin-x64": "0.19.11", 769 | "@esbuild/freebsd-arm64": "0.19.11", 770 | "@esbuild/freebsd-x64": "0.19.11", 771 | "@esbuild/linux-arm": "0.19.11", 772 | "@esbuild/linux-arm64": "0.19.11", 773 | "@esbuild/linux-ia32": "0.19.11", 774 | "@esbuild/linux-loong64": "0.19.11", 775 | "@esbuild/linux-mips64el": "0.19.11", 776 | "@esbuild/linux-ppc64": "0.19.11", 777 | "@esbuild/linux-riscv64": "0.19.11", 778 | "@esbuild/linux-s390x": "0.19.11", 779 | "@esbuild/linux-x64": "0.19.11", 780 | "@esbuild/netbsd-x64": "0.19.11", 781 | "@esbuild/openbsd-x64": "0.19.11", 782 | "@esbuild/sunos-x64": "0.19.11", 783 | "@esbuild/win32-arm64": "0.19.11", 784 | "@esbuild/win32-ia32": "0.19.11", 785 | "@esbuild/win32-x64": "0.19.11" 786 | } 787 | }, 788 | "node_modules/estree-walker": { 789 | "version": "3.0.3", 790 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", 791 | "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", 792 | "dev": true, 793 | "dependencies": { 794 | "@types/estree": "^1.0.0" 795 | } 796 | }, 797 | "node_modules/fsevents": { 798 | "version": "2.3.3", 799 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 800 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 801 | "dev": true, 802 | "hasInstallScript": true, 803 | "optional": true, 804 | "os": [ 805 | "darwin" 806 | ], 807 | "engines": { 808 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 809 | } 810 | }, 811 | "node_modules/is-reference": { 812 | "version": "3.0.2", 813 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", 814 | "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", 815 | "dev": true, 816 | "dependencies": { 817 | "@types/estree": "*" 818 | } 819 | }, 820 | "node_modules/kleur": { 821 | "version": "4.1.5", 822 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", 823 | "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", 824 | "dev": true, 825 | "engines": { 826 | "node": ">=6" 827 | } 828 | }, 829 | "node_modules/locate-character": { 830 | "version": "3.0.0", 831 | "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", 832 | "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", 833 | "dev": true 834 | }, 835 | "node_modules/magic-string": { 836 | "version": "0.30.5", 837 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", 838 | "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", 839 | "dev": true, 840 | "dependencies": { 841 | "@jridgewell/sourcemap-codec": "^1.4.15" 842 | }, 843 | "engines": { 844 | "node": ">=12" 845 | } 846 | }, 847 | "node_modules/mdn-data": { 848 | "version": "2.0.30", 849 | "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", 850 | "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", 851 | "dev": true 852 | }, 853 | "node_modules/ms": { 854 | "version": "2.1.2", 855 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 856 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 857 | "dev": true 858 | }, 859 | "node_modules/nanoid": { 860 | "version": "3.3.7", 861 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", 862 | "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", 863 | "dev": true, 864 | "funding": [ 865 | { 866 | "type": "github", 867 | "url": "https://github.com/sponsors/ai" 868 | } 869 | ], 870 | "bin": { 871 | "nanoid": "bin/nanoid.cjs" 872 | }, 873 | "engines": { 874 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 875 | } 876 | }, 877 | "node_modules/periscopic": { 878 | "version": "3.1.0", 879 | "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", 880 | "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", 881 | "dev": true, 882 | "dependencies": { 883 | "@types/estree": "^1.0.0", 884 | "estree-walker": "^3.0.0", 885 | "is-reference": "^3.0.0" 886 | } 887 | }, 888 | "node_modules/picocolors": { 889 | "version": "1.0.0", 890 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 891 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 892 | "dev": true 893 | }, 894 | "node_modules/postcss": { 895 | "version": "8.4.33", 896 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", 897 | "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", 898 | "dev": true, 899 | "funding": [ 900 | { 901 | "type": "opencollective", 902 | "url": "https://opencollective.com/postcss/" 903 | }, 904 | { 905 | "type": "tidelift", 906 | "url": "https://tidelift.com/funding/github/npm/postcss" 907 | }, 908 | { 909 | "type": "github", 910 | "url": "https://github.com/sponsors/ai" 911 | } 912 | ], 913 | "dependencies": { 914 | "nanoid": "^3.3.7", 915 | "picocolors": "^1.0.0", 916 | "source-map-js": "^1.0.2" 917 | }, 918 | "engines": { 919 | "node": "^10 || ^12 || >=14" 920 | } 921 | }, 922 | "node_modules/rollup": { 923 | "version": "4.9.4", 924 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.4.tgz", 925 | "integrity": "sha512-2ztU7pY/lrQyXSCnnoU4ICjT/tCG9cdH3/G25ERqE3Lst6vl2BCM5hL2Nw+sslAvAf+ccKsAq1SkKQALyqhR7g==", 926 | "dev": true, 927 | "dependencies": { 928 | "@types/estree": "1.0.5" 929 | }, 930 | "bin": { 931 | "rollup": "dist/bin/rollup" 932 | }, 933 | "engines": { 934 | "node": ">=18.0.0", 935 | "npm": ">=8.0.0" 936 | }, 937 | "optionalDependencies": { 938 | "@rollup/rollup-android-arm-eabi": "4.9.4", 939 | "@rollup/rollup-android-arm64": "4.9.4", 940 | "@rollup/rollup-darwin-arm64": "4.9.4", 941 | "@rollup/rollup-darwin-x64": "4.9.4", 942 | "@rollup/rollup-linux-arm-gnueabihf": "4.9.4", 943 | "@rollup/rollup-linux-arm64-gnu": "4.9.4", 944 | "@rollup/rollup-linux-arm64-musl": "4.9.4", 945 | "@rollup/rollup-linux-riscv64-gnu": "4.9.4", 946 | "@rollup/rollup-linux-x64-gnu": "4.9.4", 947 | "@rollup/rollup-linux-x64-musl": "4.9.4", 948 | "@rollup/rollup-win32-arm64-msvc": "4.9.4", 949 | "@rollup/rollup-win32-ia32-msvc": "4.9.4", 950 | "@rollup/rollup-win32-x64-msvc": "4.9.4", 951 | "fsevents": "~2.3.2" 952 | } 953 | }, 954 | "node_modules/source-map-js": { 955 | "version": "1.0.2", 956 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 957 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 958 | "dev": true, 959 | "engines": { 960 | "node": ">=0.10.0" 961 | } 962 | }, 963 | "node_modules/svelte": { 964 | "version": "4.2.8", 965 | "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.8.tgz", 966 | "integrity": "sha512-hU6dh1MPl8gh6klQZwK/n73GiAHiR95IkFsesLPbMeEZi36ydaXL/ZAb4g9sayT0MXzpxyZjR28yderJHxcmYA==", 967 | "dev": true, 968 | "dependencies": { 969 | "@ampproject/remapping": "^2.2.1", 970 | "@jridgewell/sourcemap-codec": "^1.4.15", 971 | "@jridgewell/trace-mapping": "^0.3.18", 972 | "acorn": "^8.9.0", 973 | "aria-query": "^5.3.0", 974 | "axobject-query": "^3.2.1", 975 | "code-red": "^1.0.3", 976 | "css-tree": "^2.3.1", 977 | "estree-walker": "^3.0.3", 978 | "is-reference": "^3.0.1", 979 | "locate-character": "^3.0.0", 980 | "magic-string": "^0.30.4", 981 | "periscopic": "^3.1.0" 982 | }, 983 | "engines": { 984 | "node": ">=16" 985 | } 986 | }, 987 | "node_modules/svelte-hmr": { 988 | "version": "0.15.3", 989 | "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz", 990 | "integrity": "sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==", 991 | "dev": true, 992 | "engines": { 993 | "node": "^12.20 || ^14.13.1 || >= 16" 994 | }, 995 | "peerDependencies": { 996 | "svelte": "^3.19.0 || ^4.0.0" 997 | } 998 | }, 999 | "node_modules/vite": { 1000 | "version": "5.0.11", 1001 | "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.11.tgz", 1002 | "integrity": "sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==", 1003 | "dev": true, 1004 | "dependencies": { 1005 | "esbuild": "^0.19.3", 1006 | "postcss": "^8.4.32", 1007 | "rollup": "^4.2.0" 1008 | }, 1009 | "bin": { 1010 | "vite": "bin/vite.js" 1011 | }, 1012 | "engines": { 1013 | "node": "^18.0.0 || >=20.0.0" 1014 | }, 1015 | "funding": { 1016 | "url": "https://github.com/vitejs/vite?sponsor=1" 1017 | }, 1018 | "optionalDependencies": { 1019 | "fsevents": "~2.3.3" 1020 | }, 1021 | "peerDependencies": { 1022 | "@types/node": "^18.0.0 || >=20.0.0", 1023 | "less": "*", 1024 | "lightningcss": "^1.21.0", 1025 | "sass": "*", 1026 | "stylus": "*", 1027 | "sugarss": "*", 1028 | "terser": "^5.4.0" 1029 | }, 1030 | "peerDependenciesMeta": { 1031 | "@types/node": { 1032 | "optional": true 1033 | }, 1034 | "less": { 1035 | "optional": true 1036 | }, 1037 | "lightningcss": { 1038 | "optional": true 1039 | }, 1040 | "sass": { 1041 | "optional": true 1042 | }, 1043 | "stylus": { 1044 | "optional": true 1045 | }, 1046 | "sugarss": { 1047 | "optional": true 1048 | }, 1049 | "terser": { 1050 | "optional": true 1051 | } 1052 | } 1053 | }, 1054 | "node_modules/vitefu": { 1055 | "version": "0.2.5", 1056 | "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", 1057 | "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", 1058 | "dev": true, 1059 | "peerDependencies": { 1060 | "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" 1061 | }, 1062 | "peerDependenciesMeta": { 1063 | "vite": { 1064 | "optional": true 1065 | } 1066 | } 1067 | } 1068 | } 1069 | } 1070 | --------------------------------------------------------------------------------