diff --git a/.env.example b/.env.example
index 6da83b5..921c359 100644
--- a/.env.example
+++ b/.env.example
@@ -1,6 +1,7 @@
# Port Configuration
PORT=3001
# PostgreSQL Configuration
+# Replace with the connection string for your database
DATABASE_URL=postgresql://username:password@localhost:5432/codepatchwork
PGDATABASE=codepatchwork
PGUSER=username
diff --git a/README.md b/README.md
index 2556d8c..834c8de 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,72 @@
# CodePatchwork π§©
+
-A visual code snippet manager that combines the visual appeal of Pinterest with the functionality of GitHub Gists. CodePatchwork transforms how developers manage code snippets by replacing scattered text files and notes with a visually appealing, searchable repository.
+[](https://github.com/hexawulf/CodePatchwork/stargazers)
+[](https://www.codepatchwork.com)
+[](https://opensource.org/licenses/MIT)
+
+**Transform your scattered code snippets into a beautiful, searchable visual library**
+
+*A visual code snippet manager that combines the visual appeal of Pinterest with the functionality of GitHub Gists.*

+
+
+## π **Try CodePatchwork Live!**
+
+
+
+### **π [π EXPERIENCE THE DEMO](https://www.codepatchwork.com) π**
+
+*See CodePatchwork in action - no installation required!*
+
+[](https://www.codepatchwork.com)
+
+
+
+---
+
+## π Table of Contents
+- [π Live Demo](#-try-codepatchwork-live)
+- [β¨ Features](#-features)
+- [π Getting Started](#-getting-started)
+- [π§ Usage](#-usage)
+- [π οΈ Technologies](#οΈ-technologies-used)
+- [π€ Contributing](#-contributing)
+
+## β‘ Quick Start
+
+Want to jump right in? **[Try the live demo](https://www.codepatchwork.com)** - no installation needed!
+
+For local development, you'll need Node.js 18+ and PostgreSQL. See detailed setup below β¬οΈ
+
## β¨ Features
-- **Visual Organization**: Manage code snippets with a Pinterest-style visual interface
-- **Syntax Highlighting**: Automatic code highlighting for 100+ programming languages
-- **Powerful Search & Filtering**: Find snippets by language, tags, or full-text search
-- **Collections**: Organize snippets into custom collections for better categorization
-- **Tags & Metadata**: Add tags and descriptions to make snippets more discoverable
-- **Authentication**: Secure sign-in with Google OAuth or email/password
-- **Responsive Design**: Works on desktop, tablet, and mobile devices
-- **Sharing**: Share snippets publicly with customizable links
-- **Import/Export**: Easily backup or migrate your snippets
-- **Dark/Light Themes**: Choose your preferred visual theme for better readability
-- **Comment System**: Collaborate and discuss code with other users
+### π¨ **Visual Experience**
+- **Pinterest-Style Interface** - Visually appealing snippet organization
+- **Syntax Highlighting** - Beautiful code display for 100+ languages
+- **Dark/Light Themes** - Customizable visual experience
+- **Responsive Design** - Perfect on desktop, tablet, and mobile
+
+### π **Organization & Discovery**
+- **Smart Search & Filtering** - Find snippets by language, tags, or content
+- **Custom Collections** - Organize snippets into themed groups
+- **Tags & Metadata** - Rich categorization and discovery
+- **Import/Export** - Easy backup and migration
+
+### π€ **Collaboration & Sharing**
+- **Public Sharing** - Share snippets with customizable links
+- **Comment System** - Collaborate and discuss code
+- **Secure Authentication** - Google OAuth or email/password
+
+## πΈ Screenshots
+
+
+*Beautiful Pinterest-style code snippet organization*
+
+
+*Elegant dark theme for comfortable coding*
## π Getting Started
@@ -139,9 +189,16 @@ A visual code snippet manager that combines the visual appeal of Pinterest with
- `npm run dev` - Start development server
- `npm run build` - Build for production
+- `npm run test:logger` - Verify Winston file logging in `dist`
- `npm run db:push` - Push schema changes to database
- `npm run db:studio` - Open Drizzle Studio to manage database
+### Quick Logger Test
+
+After running `npm run build`, execute `npm run test:logger` to verify that
+`/home/zk/logs/codepatchwork.log` is created. The test script writes a few
+messages using the bundled logger to ensure file logging works in production.
+
## π License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -154,6 +211,18 @@ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for det
For questions or support, please open an issue on the GitHub repository.
+## π **Ready to Transform Your Code Snippets?**
+
+
+
+### **[π Try CodePatchwork Now](https://www.codepatchwork.com)**
+
+[](https://github.com/hexawulf/CodePatchwork)
+[](https://github.com/hexawulf/CodePatchwork/issues)
+[](https://github.com/hexawulf/CodePatchwork/issues)
+
+
+
---
Made with β€οΈ by [hexawulf](https://github.com/hexawulf)
diff --git a/client/index.html b/client/index.html
index 78e48db..48907d9 100644
--- a/client/index.html
+++ b/client/index.html
@@ -11,6 +11,13 @@
+
+
+
+
+
+
+
diff --git a/client/src/components/Logo.tsx b/client/src/components/Logo.tsx
new file mode 100644
index 0000000..022ceae
--- /dev/null
+++ b/client/src/components/Logo.tsx
@@ -0,0 +1,46 @@
+// src/components/Logo.tsx
+import React from 'react';
+
+interface LogoProps {
+ size?: number;
+ className?: string;
+}
+
+export const CodePatchworkLogo: React.FC = ({
+ size = 32,
+ className = ""
+}) => {
+ return (
+
+
+
+
+
+
+
+ {"{}"}
+ {"<>"}
+ //
+ []
+ JS
+ PY
+ 01
+ .PY
+ {"β"}
+
+ );
+};
diff --git a/client/src/main.tsx b/client/src/main.tsx
index e92b8e7..bf2c5da 100644
--- a/client/src/main.tsx
+++ b/client/src/main.tsx
@@ -7,6 +7,11 @@ import { AuthProvider } from "./contexts/AuthContext";
import { QueryClientProvider } from "@tanstack/react-query";
import { queryClient } from "@/lib/queryClient";
+// Disable console.log in production to avoid verbose output
+if (process.env.NODE_ENV === "production") {
+ console.log = () => {};
+}
+
const root = createRoot(document.getElementById("root")!);
root.render(
diff --git a/codepatchwork-db-export.zip b/codepatchwork-db-export.zip
deleted file mode 100644
index debbe75..0000000
Binary files a/codepatchwork-db-export.zip and /dev/null differ
diff --git a/db-export/README.md b/db-export/README.md
deleted file mode 100644
index 70ac84c..0000000
--- a/db-export/README.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# CodePatchwork Database Migration
-
-This folder contains database export files to migrate your CodePatchwork database to a local PostgreSQL instance.
-
-## Files
-
-- `schema.sql`: Database schema definition (tables, constraints, etc.)
-- `data.sql`: Database data only (no schema)
-- `complete_dump.dump`: Complete database export in PostgreSQL custom format
-
-## Import Instructions
-
-### Option 1: Using the complete dump (recommended)
-
-1. Create a new database:
- ```bash
- createdb codepatchwork
- ```
-
-2. Import the complete dump:
- ```bash
- pg_restore -d codepatchwork --no-owner --role=your_username complete_dump.dump
- ```
-
-### Option 2: Using separate schema and data files
-
-1. Create a new database:
- ```bash
- createdb codepatchwork
- ```
-
-2. Import the schema:
- ```bash
- psql -d codepatchwork -f schema.sql
- ```
-
-3. Import the data:
- ```bash
- psql -d codepatchwork -f data.sql
- ```
-
-## Database Configuration
-
-After import, update your local .env file with:
-
-```
-DATABASE_URL=postgres://your_username:your_password@localhost:5432/codepatchwork
-```
-
-Replace `your_username` and `your_password` with your local PostgreSQL credentials.
diff --git a/db-export/complete_dump.dump b/db-export/complete_dump.dump
deleted file mode 100644
index 15b6399..0000000
Binary files a/db-export/complete_dump.dump and /dev/null differ
diff --git a/db-export/data.sql b/db-export/data.sql
deleted file mode 100644
index 9545b4f..0000000
--- a/db-export/data.sql
+++ /dev/null
@@ -1,23 +0,0 @@
---
--- PostgreSQL database dump
---
-
--- Dumped from database version 17.4 (Ubuntu 17.4-1)
--- Dumped by pg_dump version 17.4 (Ubuntu 17.4-1)
-
-SET statement_timeout = 0;
-SET lock_timeout = 0;
-SET idle_in_transaction_session_timeout = 0;
-SET transaction_timeout = 0;
-SET client_encoding = 'UTF8';
-SET standard_conforming_strings = on;
-SELECT pg_catalog.set_config('search_path', '', false);
-SET check_function_bodies = false;
-SET xmloption = content;
-SET client_min_messages = warning;
-SET row_security = off;
-
---
--- PostgreSQL database dump complete
---
-
diff --git a/db-export/schema.sql b/db-export/schema.sql
deleted file mode 100644
index e69de29..0000000
diff --git a/db-test.js b/db-test.js
deleted file mode 100644
index dc6fa1b..0000000
--- a/db-test.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import { config } from 'dotenv';
-import pg from 'pg';
-const { Pool } = pg;
-
-// Load environment variables
-config();
-
-const pool = new Pool({
- connectionString: process.env.DATABASE_URL
-});
-
-pool.query('SELECT NOW()', (err, res) => {
- if (err) {
- console.error('Error connecting to database:', err);
- } else {
- console.log('Successfully connected to PostgreSQL!');
- console.log('Current time from database:', res.rows[0].now);
- }
- pool.end();
-});
diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs
index 6417c2b..8a6c64e 100644
--- a/ecosystem.config.cjs
+++ b/ecosystem.config.cjs
@@ -1,16 +1,12 @@
module.exports = {
- apps: [{
- name: "codepatchwork",
- script: "npm",
- args: "start",
- cwd: "/home/zk/projects/CodePatchwork",
- env: {
- NODE_ENV: "production",
- PORT: 3001 // Add this line
- },
- instances: 1,
- autorestart: true,
- watch: false,
- max_memory_restart: "1G"
- }]
+ apps: [
+ {
+ name: "codepatchwork",
+ script: "dist/index.js",
+ node_args: "-r dotenv/config",
+ env: {
+ NODE_ENV: "production"
+ }
+ }
+ ]
};
diff --git a/firebase.bk b/firebase.bk
deleted file mode 100644
index f055770..0000000
--- a/firebase.bk
+++ /dev/null
@@ -1,107 +0,0 @@
-// client/src/lib/firebase.ts
-/* ------------------------------------------------------------------
- * Firebase bootstrap for CodePatchwork
- * ------------------------------------------------------------------ */
-
-import { initializeApp, getApps, getApp } from "firebase/app";
-import {
- getAuth,
- GoogleAuthProvider,
- onAuthStateChanged,
- User,
-} from "firebase/auth";
-
-// 1οΈβ£ Pull the seven required VITE_ env-vars out of import.meta.env
-const {
- VITE_FIREBASE_API_KEY,
- VITE_FIREBASE_AUTH_DOMAIN,
- VITE_FIREBASE_PROJECT_ID,
- VITE_FIREBASE_STORAGE_BUCKET,
- VITE_FIREBASE_MESSAGING_SENDER_ID,
- VITE_FIREBASE_APP_ID,
- VITE_FIREBASE_MEASUREMENT_ID,
-} = import.meta.env;
-
-// 2οΈβ£ Sanity-check: error if any of the βmust haveβ values is missing
-if (
- !VITE_FIREBASE_API_KEY ||
- !VITE_FIREBASE_AUTH_DOMAIN ||
- !VITE_FIREBASE_PROJECT_ID
-) {
- throw new Error(
- "[Firebase] Missing required VITE_FIREBASE_* env vars. " +
- "Make sure your .env is loading them."
- );
-}
-
-// 3οΈβ£ Build your config object
-const firebaseConfig = {
- apiKey: VITE_FIREBASE_API_KEY,
- authDomain: VITE_FIREBASE_AUTH_DOMAIN,
- projectId: VITE_FIREBASE_PROJECT_ID,
- storageBucket: VITE_FIREBASE_STORAGE_BUCKET,
- messagingSenderId: VITE_FIREBASE_MESSAGING_SENDER_ID,
- appId: VITE_FIREBASE_APP_ID,
- measurementId: VITE_FIREBASE_MEASUREMENT_ID,
-};
-
-// 4οΈβ£ Debug log so you can see exactly what shipped in your bundle
-console.log("%c[Firebase cfg]", "color:#4ade80;", firebaseConfig);
-
-// 5οΈβ£ Initialise the app (avoiding duplicates on hot-reload)
-const app = getApps().length ? getApp() : initializeApp(firebaseConfig);
-
-// 6οΈβ£ Set up Auth + Google provider
-const auth = getAuth(app);
-const googleProvider = new GoogleAuthProvider();
-
-// 7οΈβ£ Listen for sign-in state changes and POST your ID token to the server
-if (typeof window !== "undefined") {
- onAuthStateChanged(auth, async (user: User | null) => {
- if (!user) {
- console.log("[Firebase] No user signed in");
- return;
- }
-
- try {
- // a) grab a fresh ID token
- const idToken = await user.getIdToken(/* forceRefresh */ true);
-
- // b) send it to your backend
- const res = await fetch("/api/auth/user", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ idToken }),
- });
-
- if (!res.ok) {
- console.error(
- "π΄ /api/auth/user failed:",
- res.status,
- await res.text()
- );
- } else {
- console.log("π’ /api/auth/user succeeded:", await res.json());
- }
- } catch (e) {
- console.error("[Firebase] Error sending ID token:", e);
- }
- });
-}
-
-// 8οΈβ£ In non-prod, expose for DevTools debugging
-if (
- import.meta.env.MODE !== "production" &&
- typeof window !== "undefined"
-) {
- // @ts-ignore
- window.__app = app;
- // @ts-ignore
- window.__auth = auth;
- // @ts-ignore
- window.__prov = googleProvider;
-}
-
-// 9οΈβ£ Export for your UI code
-export { app, auth, googleProvider };
-export type { User as FirebaseUser };
diff --git a/output.html b/output.html
deleted file mode 100644
index 62cd9f2..0000000
--- a/output.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
- CodePatchwork - Visual Code Snippet Manager
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/package-lock.json b/package-lock.json
index 0416492..86b0746 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -65,6 +65,7 @@
"openid-client": "^6.5.0",
"passport": "^0.7.0",
"passport-local": "^1.0.0",
+ "pg": "^8.12.0",
"prism-react-renderer": "^2.4.1",
"prismjs": "^1.30.0",
"react": "^18.3.1",
@@ -79,6 +80,7 @@
"tailwindcss-animate": "^1.0.7",
"tw-animate-css": "^1.2.5",
"vaul": "^1.1.2",
+ "winston": "^3.17.0",
"wouter": "^3.3.5",
"ws": "^8.18.0",
"zod": "^3.24.2",
@@ -412,6 +414,24 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@colors/colors": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+ "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/@dabh/diagnostics": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
+ "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
+ "dependencies": {
+ "colorspace": "1.1.x",
+ "enabled": "2.0.x",
+ "kuler": "^2.0.0"
+ }
+ },
"node_modules/@drizzle-team/brocli": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz",
@@ -4648,6 +4668,11 @@
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
"optional": true
},
+ "node_modules/@types/triple-beam": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
+ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
+ },
"node_modules/@types/ws": {
"version": "8.5.13",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
@@ -6828,6 +6853,46 @@
"node": ">=0.1.90"
}
},
+ "node_modules/colorspace": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
+ "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
+ "dependencies": {
+ "color": "^3.1.3",
+ "text-hex": "1.0.x"
+ }
+ },
+ "node_modules/colorspace/node_modules/color": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
+ "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
+ "dependencies": {
+ "color-convert": "^1.9.3",
+ "color-string": "^1.6.0"
+ }
+ },
+ "node_modules/colorspace/node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/colorspace/node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+ },
+ "node_modules/colorspace/node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -8406,6 +8471,11 @@
"node": ">= 4"
}
},
+ "node_modules/enabled": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
+ "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
+ },
"node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
@@ -9228,6 +9298,11 @@
"node": ">=0.8.0"
}
},
+ "node_modules/fecha": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
+ },
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@@ -9421,6 +9496,11 @@
"deprecated": "flatten is deprecated in favor of utility frameworks such as lodash.",
"license": "MIT"
},
+ "node_modules/fn.name": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
+ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
+ },
"node_modules/for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@@ -11057,6 +11137,11 @@
"node": ">=0.10.0"
}
},
+ "node_modules/kuler": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
+ },
"node_modules/lazy-cache": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
@@ -11529,6 +11614,22 @@
"integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
"license": "MIT"
},
+ "node_modules/logform": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
+ "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
+ "dependencies": {
+ "@colors/colors": "1.6.0",
+ "@types/triple-beam": "^1.3.2",
+ "fecha": "^4.2.0",
+ "ms": "^2.1.1",
+ "safe-stable-stringify": "^2.3.1",
+ "triple-beam": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
"node_modules/long": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
@@ -12454,6 +12555,14 @@
"wrappy": "1"
}
},
+ "node_modules/one-time": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
+ "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+ "dependencies": {
+ "fn.name": "1.x.x"
+ }
+ },
"node_modules/openid-client": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.5.0.tgz",
@@ -15812,6 +15921,14 @@
"ret": "~0.1.10"
}
},
+ "node_modules/safe-stable-stringify": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -16058,6 +16175,19 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/simple-swizzle/node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+ },
"node_modules/slash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
@@ -16375,6 +16505,14 @@
"node": ">=0.8"
}
},
+ "node_modules/stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/static-extend": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
@@ -16787,6 +16925,11 @@
"uuid": "dist/bin/uuid"
}
},
+ "node_modules/text-hex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
+ },
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
@@ -16986,6 +17129,14 @@
"node": ">=0.10.0"
}
},
+ "node_modules/triple-beam": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
+ "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
@@ -18590,6 +18741,82 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/winston": {
+ "version": "3.17.0",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz",
+ "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==",
+ "dependencies": {
+ "@colors/colors": "^1.6.0",
+ "@dabh/diagnostics": "^2.0.2",
+ "async": "^3.2.3",
+ "is-stream": "^2.0.0",
+ "logform": "^2.7.0",
+ "one-time": "^1.0.0",
+ "readable-stream": "^3.4.0",
+ "safe-stable-stringify": "^2.3.1",
+ "stack-trace": "0.0.x",
+ "triple-beam": "^1.3.0",
+ "winston-transport": "^4.9.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/winston-transport": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
+ "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
+ "dependencies": {
+ "logform": "^2.7.0",
+ "readable-stream": "^3.6.2",
+ "triple-beam": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/winston-transport/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/winston/node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="
+ },
+ "node_modules/winston/node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/winston/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/wordwrap": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
diff --git a/package.json b/package.json
index d63636b..1118970 100644
--- a/package.json
+++ b/package.json
@@ -3,9 +3,11 @@
"version": "1.0.0",
"type": "module",
"license": "MIT",
+ "sideEffects": ["./server/logger.ts"],
"scripts": {
"dev": "NODE_ENV=development tsx server/index.ts",
- "build": "vite build && esbuild server/index.ts --platform=node --packages=external --bundle --format=esm --outdir=dist",
+ "build": "vite build && esbuild server/index.ts server/winston-test.ts --platform=node --packages=external --bundle --format=esm --outdir=dist --out-extension:.js=.js --tree-shaking=false",
+ "test:logger": "node dist/winston-test.js",
"start": "NODE_ENV=production node -r dotenv/config dist/index.js",
"check": "tsc",
"db:push": "drizzle-kit push"
@@ -67,6 +69,7 @@
"openid-client": "^6.5.0",
"passport": "^0.7.0",
"passport-local": "^1.0.0",
+ "pg": "^8.12.0",
"prism-react-renderer": "^2.4.1",
"prismjs": "^1.30.0",
"react": "^18.3.1",
@@ -81,6 +84,7 @@
"tailwindcss-animate": "^1.0.7",
"tw-animate-css": "^1.2.5",
"vaul": "^1.1.2",
+ "winston": "^3.17.0",
"wouter": "^3.3.5",
"ws": "^8.18.0",
"zod": "^3.24.2",
diff --git a/package.txt b/package.txt
deleted file mode 100644
index 3fe36ce..0000000
--- a/package.txt
+++ /dev/null
@@ -1,113 +0,0 @@
-{
- "name": "rest-express",
- "version": "1.0.0",
- "type": "module",
- "license": "MIT",
- "scripts": {
- "dev": "NODE_ENV=development tsx server/index.ts",
- "build": "vite build && esbuild server/index.ts --platform=node --packages=external --bundle --format=esm --outdir=dist",
- "start": "NODE_ENV=production node -r dotenv/config dist/index.js"
- "check": "tsc",
- "db:push": "drizzle-kit push"
- },
- "dependencies": {
- "@hookform/resolvers": "^3.10.0",
- "@jridgewell/trace-mapping": "^0.3.25",
- "@neondatabase/serverless": "^0.10.4",
- "@radix-ui/react-accordion": "^1.2.4",
- "@radix-ui/react-alert-dialog": "^1.1.7",
- "@radix-ui/react-aspect-ratio": "^1.1.3",
- "@radix-ui/react-avatar": "^1.1.4",
- "@radix-ui/react-checkbox": "^1.1.5",
- "@radix-ui/react-collapsible": "^1.1.4",
- "@radix-ui/react-context-menu": "^2.2.7",
- "@radix-ui/react-dialog": "^1.1.7",
- "@radix-ui/react-dropdown-menu": "^2.1.7",
- "@radix-ui/react-hover-card": "^1.1.7",
- "@radix-ui/react-label": "^2.1.3",
- "@radix-ui/react-menubar": "^1.1.7",
- "@radix-ui/react-navigation-menu": "^1.2.6",
- "@radix-ui/react-popover": "^1.1.7",
- "@radix-ui/react-progress": "^1.1.3",
- "@radix-ui/react-radio-group": "^1.2.4",
- "@radix-ui/react-scroll-area": "^1.2.4",
- "@radix-ui/react-select": "^2.1.7",
- "@radix-ui/react-separator": "^1.1.3",
- "@radix-ui/react-slider": "^1.2.4",
- "@radix-ui/react-slot": "^1.2.0",
- "@radix-ui/react-switch": "^1.1.4",
- "@radix-ui/react-tabs": "^1.1.4",
- "@radix-ui/react-toast": "^1.2.7",
- "@radix-ui/react-toggle": "^1.1.3",
- "@radix-ui/react-toggle-group": "^1.1.3",
- "@radix-ui/react-tooltip": "^1.2.0",
- "@tanstack/react-query": "^5.60.5",
- "@types/memoizee": "^0.4.12",
- "class-variance-authority": "^0.7.1",
- "clsx": "^2.1.1",
- "cmdk": "^1.1.1",
- "connect-pg-simple": "^10.0.0",
- "date-fns": "^3.6.0",
- "dotenv": "^16.5.0",
- "drizzle-orm": "^0.39.1",
- "drizzle-zod": "^0.7.0",
- "embla-carousel-react": "^8.6.0",
- "express": "^4.21.2",
- "express-session": "^1.18.1",
- "firebase": "^11.7.3",
- "framer-motion": "^11.13.1",
- "input-otp": "^1.4.2",
- "lucide-react": "^0.453.0",
- "memoizee": "^0.4.17",
- "memorystore": "^1.6.7",
- "next-themes": "^0.4.6",
- "openid-client": "^6.5.0",
- "passport": "^0.7.0",
- "passport-local": "^1.0.0",
- "prism-react-renderer": "^2.4.1",
- "prismjs": "^1.30.0",
- "react": "^18.3.1",
- "react-day-picker": "^8.10.1",
- "react-dom": "^18.3.1",
- "react-hook-form": "^7.55.0",
- "react-icons": "^5.4.0",
- "react-prismjs": "^1.0.4",
- "react-resizable-panels": "^2.1.7",
- "recharts": "^2.15.2",
- "tailwind-merge": "^2.6.0",
- "tailwindcss-animate": "^1.0.7",
- "tw-animate-css": "^1.2.5",
- "vaul": "^1.1.2",
- "wouter": "^3.3.5",
- "ws": "^8.18.0",
- "zod": "^3.24.2",
- "zod-validation-error": "^3.4.0"
- },
- "devDependencies": {
- "@replit/vite-plugin-cartographer": "^0.2.0",
- "@replit/vite-plugin-runtime-error-modal": "^0.0.3",
- "@tailwindcss/typography": "^0.5.15",
- "@tailwindcss/vite": "^4.1.3",
- "@types/connect-pg-simple": "^7.0.3",
- "@types/express": "4.17.21",
- "@types/express-session": "^1.18.0",
- "@types/node": "20.16.11",
- "@types/passport": "^1.0.16",
- "@types/passport-local": "^1.0.38",
- "@types/react": "^18.3.11",
- "@types/react-dom": "^18.3.1",
- "@types/ws": "^8.5.13",
- "@vitejs/plugin-react": "^4.3.2",
- "autoprefixer": "^10.4.20",
- "drizzle-kit": "^0.30.6",
- "esbuild": "^0.25.0",
- "postcss": "^8.4.47",
- "tailwindcss": "^3.4.17",
- "tsx": "^4.19.1",
- "typescript": "5.6.3",
- "vite": "^5.4.14"
- },
- "optionalDependencies": {
- "bufferutil": "^4.0.8"
- }
-}
diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png
new file mode 100644
index 0000000..c9dc5b7
Binary files /dev/null and b/public/apple-touch-icon.png differ
diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png
new file mode 100644
index 0000000..4af04a7
Binary files /dev/null and b/public/favicon-16x16.png differ
diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png
new file mode 100644
index 0000000..7c1449f
Binary files /dev/null and b/public/favicon-32x32.png differ
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..da914d4
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/public/favicon.svg b/public/favicon.svg
new file mode 100644
index 0000000..ee0fd08
--- /dev/null
+++ b/public/favicon.svg
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {}
+ <>
+ //
+ []
+ JS
+ PY
+ 01
+ .PY
+ ⎇
+
diff --git a/server/db.js b/server/db.js
deleted file mode 100644
index 99b25ed..0000000
--- a/server/db.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Pool, neonConfig } from '@neondatabase/serverless';
-import { drizzle } from 'drizzle-orm/neon-serverless';
-import ws from "ws";
-import * as schema from "@shared/schema";
-neonConfig.webSocketConstructor = ws;
-if (!process.env.DATABASE_URL) {
- throw new Error("DATABASE_URL must be set. Did you forget to provision a database?");
-}
-export var pool = new Pool({ connectionString: process.env.DATABASE_URL });
-export var db = drizzle(pool, { schema: schema });
diff --git a/server/debug_storage.js b/server/debug_storage.js
deleted file mode 100644
index 7441be7..0000000
--- a/server/debug_storage.js
+++ /dev/null
@@ -1,88 +0,0 @@
-var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
- return new (P || (P = Promise))(function (resolve, reject) {
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
- step((generator = generator.apply(thisArg, _arguments || [])).next());
- });
-};
-var __generator = (this && this.__generator) || function (thisArg, body) {
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
- function verb(n) { return function (v) { return step([n, v]); }; }
- function step(op) {
- if (f) throw new TypeError("Generator is already executing.");
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
- if (y = 0, t) op = [op[0] & 2, t.value];
- switch (op[0]) {
- case 0: case 1: t = op; break;
- case 4: _.label++; return { value: op[1], done: false };
- case 5: _.label++; y = op[1]; op = [0]; continue;
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
- default:
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
- if (t[2]) _.ops.pop();
- _.trys.pop(); continue;
- }
- op = body.call(thisArg, _);
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
- }
-};
-import { pool } from './db';
-import { DatabaseStorage } from './storage';
-function testDatabaseStorage() {
- return __awaiter(this, void 0, void 0, function () {
- var storage, snippets, tags, languages, collections, error_1;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0:
- console.log('Testing DatabaseStorage...');
- storage = new DatabaseStorage();
- _a.label = 1;
- case 1:
- _a.trys.push([1, 6, 7, 9]);
- console.log('Testing getSnippets()...');
- return [4 /*yield*/, storage.getSnippets()];
- case 2:
- snippets = _a.sent();
- console.log("Found ".concat(snippets.length, " snippets"));
- console.log('Testing getTags()...');
- return [4 /*yield*/, storage.getTags()];
- case 3:
- tags = _a.sent();
- console.log("Found ".concat(tags.length, " tags: ").concat(tags.join(', ')));
- console.log('Testing getLanguages()...');
- return [4 /*yield*/, storage.getLanguages()];
- case 4:
- languages = _a.sent();
- console.log("Found ".concat(languages.length, " languages: ").concat(languages.join(', ')));
- console.log('Testing getCollections()...');
- return [4 /*yield*/, storage.getCollections()];
- case 5:
- collections = _a.sent();
- console.log("Found ".concat(collections.length, " collections"));
- return [3 /*break*/, 9];
- case 6:
- error_1 = _a.sent();
- console.error('Error testing DatabaseStorage:', error_1);
- return [3 /*break*/, 9];
- case 7:
- // Close the database connection pool
- return [4 /*yield*/, pool.end()];
- case 8:
- // Close the database connection pool
- _a.sent();
- return [7 /*endfinally*/];
- case 9: return [2 /*return*/];
- }
- });
- });
-}
-// Run the test
-testDatabaseStorage();
diff --git a/server/index.ts b/server/index.ts
index cc36b70..3ef53b4 100644
--- a/server/index.ts
+++ b/server/index.ts
@@ -1,5 +1,4 @@
-// server/index.ts β Express bootstrap for CodePatchwork
-/* ------------------------------------------------------------------ */
+// server/index.ts β Express bootstrap for CodePatchwork with Winston logging
import dotenv from "dotenv";
dotenv.config();
@@ -8,22 +7,36 @@ import fs from "fs";
import path from "path";
import admin from "firebase-admin";
import express, { Request, Response, NextFunction } from "express";
-import camelCase from "camelcase";
import helmet from "helmet";
+import camelCase from "camelcase";
import { registerRoutes } from "./routes";
-import { setupVite, serveStatic, log } from "./vite";
+import { setupVite, serveStatic } from "./vite";
+import logger from "./logger";
+import "./logger.ts"; // force esbuild to preserve it
+
+// Disable console.log in production to avoid verbose output
+if (process.env.NODE_ENV === "production") {
+ console.log = () => {};
+}
+
+
+/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
+/* 0. Winston test log β confirms logger is active */
+/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
+logger.info("β
Winston logger loaded from ./logger.ts");
+logger.info("π§ͺ Logger test: Express server startup log");
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
-/* 0. Verify & load your service-account JSON */
+/* 1. Verify & load your service-account JSON */
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
const svcPath = path.resolve(
process.cwd(),
process.env.GOOGLE_APPLICATION_CREDENTIALS!
);
-console.log("β SERVICE ACCOUNT path:", svcPath);
-console.log("β Exists on disk? ", fs.existsSync(svcPath));
+logger.info(`β SERVICE ACCOUNT path: ${svcPath}`);
+logger.info(`β Exists on disk? ${fs.existsSync(svcPath)}`);
if (!fs.existsSync(svcPath)) {
- console.error("β service account JSON not found. Aborting.");
+ logger.error("β service account JSON not found. Aborting.");
process.exit(1);
}
@@ -32,36 +45,32 @@ const serviceAccount = JSON.parse(
);
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
-/* 1. Initialize Firebase Admin with explicit cert */
+/* 2. Initialize Firebase Admin */
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
-/* 2. Express + Body parsers */
+/* 3. Express setup */
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
-/* 3. Security headers - Modified to fix Firebase auth */
+/* 4. Helmet security headers */
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
-// Disable all helmet protections and apply only what we need
app.use(
- // Completely disable COOP and COEP policies for auth popups
helmet.crossOriginOpenerPolicy({ policy: "unsafe-none" }),
helmet.crossOriginEmbedderPolicy({ policy: "unsafe-none" }),
-
- // Apply minimum security headers to allow Firebase auth
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
- "'self'",
+ "'self'",
"'unsafe-inline'",
- "'unsafe-eval'", // Needed for some Firebase operations
+ "'unsafe-eval'",
"https://www.gstatic.com",
"https://apis.google.com",
"https://*.firebaseio.com",
@@ -88,23 +97,19 @@ app.use(
);
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
-/* 4. CRITICAL FIX: JSON-only middleware for ALL API routes */
+/* 5. API middleware */
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
app.use('/api', (req, res, next) => {
- console.log(`[API] ${req.method} ${req.path} - Request received`);
-
- // Force Content-Type to application/json for all API responses
+ logger.info(`[API] ${req.method} ${req.path} - Request received`);
+
res.setHeader('Content-Type', 'application/json');
-
- // Override Express's default error handling to ensure JSON responses
+
const originalSend = res.send;
const originalStatus = res.status;
-
- // Ensure res.send always returns JSON for API routes
+
res.send = function(data: any) {
- // If Express tries to send HTML (like error pages), convert to JSON
if (typeof data === 'string' && (data.includes(''))) {
- console.log(`[API] π¨ Converting HTML response to JSON for ${req.method} ${req.path}`);
+ logger.info(`[API] π¨ Converting HTML response to JSON for ${req.method} ${req.path}`);
this.setHeader('Content-Type', 'application/json');
return originalSend.call(this, JSON.stringify({
message: "API endpoint error",
@@ -116,16 +121,13 @@ app.use('/api', (req, res, next) => {
}
return originalSend.call(this, data);
};
-
- // Override res.status to ensure chaining works with JSON
+
res.status = function(statusCode: number) {
const result = originalStatus.call(this, statusCode);
-
- // If someone calls res.status().send() with HTML, intercept it
const newSend = result.send;
result.send = function(data: any) {
if (typeof data === 'string' && (data.includes(''))) {
- console.log(`[API] π¨ Converting status ${statusCode} HTML to JSON for ${req.method} ${req.path}`);
+ logger.info(`[API] π¨ Converting status ${statusCode} HTML to JSON for ${req.method} ${req.path}`);
this.setHeader('Content-Type', 'application/json');
return originalSend.call(this, JSON.stringify({
message: "API Error",
@@ -137,15 +139,14 @@ app.use('/api', (req, res, next) => {
}
return newSend.call(this, data);
};
-
return result;
};
-
+
next();
});
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
-/* 5. Normalize /api/snippets payload (snake β camel, ISO dates) */
+/* 6. Normalize response keys */
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
app.use("/api/snippets", (_req, res, next) => {
type Row = Record;
@@ -174,7 +175,7 @@ app.use("/api/snippets", (_req, res, next) => {
});
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
-/* 6. Simple API request logger */
+/* 7. Performance + payload logging */
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
app.use((req, res, next) => {
const t0 = Date.now();
@@ -192,7 +193,7 @@ app.use((req, res, next) => {
let msg = `${req.method} ${req.path} ${res.statusCode} in ${ms}ms`;
if (payload) msg += ` :: ${JSON.stringify(payload)}`;
if (msg.length > 80) msg = msg.slice(0, 79) + "β¦";
- log(msg);
+ logger.info(msg);
}
});
@@ -200,19 +201,16 @@ app.use((req, res, next) => {
});
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
-/* 7. Route registration */
+/* 8. Route registration + boot */
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
(async () => {
- console.log("π§ Starting route registration...");
+ logger.info("π§ Starting route registration...");
const server = await registerRoutes(app);
- console.log("β
Route registration complete");
+ logger.info("β
Route registration complete");
- /* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
- /* 8. 404 Handler for unmatched API routes - MUST BE BEFORE GLOBAL */
- /* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
app.use('/api/*', (req, res) => {
- console.log(`[404] API route not found: ${req.method} ${req.path}`);
- res.status(404).json({
+ logger.info(`[404] API route not found: ${req.method} ${req.path}`);
+ res.status(404).json({
message: "API endpoint not found",
path: req.path,
method: req.method,
@@ -222,7 +220,7 @@ app.use((req, res, next) => {
"GET /api/snippets",
"GET /api/snippets/:id",
"POST /api/snippets",
- "PUT /api/snippets/:id",
+ "PUT /api/snippets/:id",
"DELETE /api/snippets/:id",
"POST /api/snippets/:id/favorite",
"GET /api/languages",
@@ -233,15 +231,9 @@ app.use((req, res, next) => {
});
});
- /* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
- /* 9. Enhanced global error handler - MUST BE AFTER 404 HANDLER */
- /* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
- console.error(`[π₯ GLOBAL ERROR] ${req.method} ${req.path}:`, err.stack || err);
-
- // Ensure we don't send if headers already sent
+ logger.error(`[π₯ GLOBAL ERROR] ${req.method} ${req.path}:`, err.stack || err);
if (!res.headersSent) {
- // Always return JSON for API routes
if (req.path.startsWith('/api/')) {
res.setHeader('Content-Type', 'application/json');
res.status(err.status || 500).json({
@@ -252,66 +244,56 @@ app.use((req, res, next) => {
error: process.env.NODE_ENV === 'production' ? 'Something went wrong' : err.stack
});
} else {
- // For non-API routes, use your original behavior
res.status(err.status || 500).json({ message: err.message || "Error" });
}
}
});
- /* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
- /* 10. Vite in dev or static in prod */
- /* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
if (app.get("env") === "development") {
await setupVite(app, server);
} else {
serveStatic(app);
}
- /* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
- /* 11. Start HTTP server */
- /* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
const port = Number(process.env.PORT) || 3001;
- server.listen(
- { host: "0.0.0.0", port, reusePort: true },
- () => {
- log(`π Serving on port ${port}`);
- log(`π‘ API available at http://localhost:${port}/api/`);
- log(`π§ͺ Test API at http://localhost:${port}/api/test`);
- console.log("---");
- console.log("π§ API Endpoints registered:");
- console.log(" GET /api/test");
- console.log(" GET /api/snippets");
- console.log(" GET /api/snippets/:id");
- console.log(" POST /api/snippets");
- console.log(" PUT /api/snippets/:id");
- console.log(" DELETE /api/snippets/:id");
- console.log(" POST /api/snippets/:id/favorite");
- console.log(" GET /api/languages");
- console.log(" GET /api/tags");
- console.log(" POST /api/auth/user");
- console.log(" GET /api/auth/me");
- console.log("---");
- }
- );
+ logger.info(`π Express server starting on port ${process.env.PORT || 3001}`);
+ server.listen({ host: "0.0.0.0", port, reusePort: true }, () => {
+ logger.info(`π Serving on port ${port}`);
+ logger.info(`π‘ API available at http://localhost:${port}/api/`);
+ logger.info(`π§ͺ Test API at http://localhost:${port}/api/test`);
+ logger.info("---");
+ logger.info("π§ API Endpoints registered:");
+ logger.info(" GET /api/test");
+ logger.info(" GET /api/snippets");
+ logger.info(" GET /api/snippets/:id");
+ logger.info(" POST /api/snippets");
+ logger.info(" PUT /api/snippets/:id");
+ logger.info(" DELETE /api/snippets/:id");
+ logger.info(" POST /api/snippets/:id/favorite");
+ logger.info(" GET /api/languages");
+ logger.info(" GET /api/tags");
+ logger.info(" POST /api/auth/user");
+ logger.info(" GET /api/auth/me");
+ logger.info("---");
+ });
- // Handle process termination gracefully
process.on('SIGTERM', () => {
- console.log('π SIGTERM received, shutting down gracefully');
+ logger.info('π SIGTERM received, shutting down gracefully');
server.close(() => {
- console.log('β
Server closed');
+ logger.info('β
Server closed');
process.exit(0);
});
});
process.on('SIGINT', () => {
- console.log('π SIGINT received, shutting down gracefully');
+ logger.info('π SIGINT received, shutting down gracefully');
server.close(() => {
- console.log('β
Server closed');
+ logger.info('β
Server closed');
process.exit(0);
});
});
})().catch((error) => {
- console.error('β Failed to start server:', error);
+ logger.error("β Failed to start server:", error);
process.exit(1);
});
diff --git a/server/logger.ts b/server/logger.ts
new file mode 100644
index 0000000..71253b8
--- /dev/null
+++ b/server/logger.ts
@@ -0,0 +1,53 @@
+import fs from 'fs';
+import path from 'path';
+import { createLogger, format, transports } from 'winston';
+
+const logDir = '/home/zk/logs';
+const logFile = path.join(logDir, 'codepatchwork.log');
+
+if (!fs.existsSync(logDir)) {
+ try {
+ fs.mkdirSync(logDir, { recursive: true });
+ console.log(`β
Created log directory: ${logDir}`);
+ } catch (err) {
+ console.error(`β Failed to create log directory: ${logDir}`, err);
+ }
+}
+
+const fileTransport = new transports.File({ filename: logFile });
+
+fileTransport.on('error', err => {
+ console.error('β οΈ File transport error:', err.message);
+ try {
+ fs.appendFileSync(
+ path.join(logDir, 'fallback.log'),
+ `[${new Date().toISOString()}] Logger failed: ${err.message}\n`
+ );
+ } catch (appendErr) {
+ console.error('β οΈ Fallback logging failed:', (appendErr as Error).message);
+ }
+});
+
+const logger = createLogger({
+ level: 'info',
+ format: format.combine(
+ format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
+ format.printf(({ timestamp, level, message, ...meta }) => {
+ const metaString = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : '';
+ return `${timestamp} [${level}]: ${message}${metaString}`;
+ })
+ ),
+ transports: [
+ new transports.Console(),
+ fileTransport
+ ]
+});
+
+logger.on('error', (err) => {
+ console.error('β Winston internal logging error:', err);
+});
+
+logger.info('π§ͺ Winston logger initialized and ready.');
+
+export default logger;
+export const __loggerSideEffect__ = true;
diff --git a/server/routes.ts b/server/routes.ts
index 5dbd24d..5df3507 100644
--- a/server/routes.ts
+++ b/server/routes.ts
@@ -6,6 +6,7 @@ import type { DecodedIdToken } from "firebase-admin/auth";
import { pool } from "./db";
import { storage } from "./storage";
import { simpleStorage } from "./simple-storage";
+import logger from "./logger";
import {
insertSnippetSchema,
insertCollectionSchema,
@@ -19,10 +20,10 @@ import { z } from "zod";
;(async () => {
try {
const client = await pool.connect();
- console.log("β
DATABASE CONNECTION TEST: OK β", (await client.query("SELECT NOW()")).rows[0].now);
+ logger.info(`β
DATABASE CONNECTION TEST: OK β ${(await client.query("SELECT NOW()")).rows[0].now}`);
client.release();
} catch (e) {
- console.error("β DATABASE CONNECTION TEST: FAILED", e);
+ logger.error("β DATABASE CONNECTION TEST: FAILED", e);
}
})();
@@ -49,6 +50,12 @@ export const authMiddleware: RequestHandler = async (req, res, next) => {
/** βββ 3) Register all routes βββββββββββββββββββββββββββββββββββββββββββββββ */
export async function registerRoutes(app: Express): Promise {
+
+
+ app.get('/api/test', (_req, res) => {
+ res.json({ message: "π§ͺ API test successful" });
+ });
+
// βββ 3.0) Health Check Endpoint ββββββββββββββββββββββββββββββββββ
app.get("/api/health", async (req: Request, res: Response) => {
diff --git a/server/storage.js b/server/storage.js
deleted file mode 100644
index 4cad0ce..0000000
--- a/server/storage.js
+++ /dev/null
@@ -1,1176 +0,0 @@
-var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
- if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
- return cooked;
-};
-var __assign = (this && this.__assign) || function () {
- __assign = Object.assign || function(t) {
- for (var s, i = 1, n = arguments.length; i < n; i++) {
- s = arguments[i];
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
- t[p] = s[p];
- }
- return t;
- };
- return __assign.apply(this, arguments);
-};
-var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
- return new (P || (P = Promise))(function (resolve, reject) {
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
- step((generator = generator.apply(thisArg, _arguments || [])).next());
- });
-};
-var __generator = (this && this.__generator) || function (thisArg, body) {
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
- function verb(n) { return function (v) { return step([n, v]); }; }
- function step(op) {
- if (f) throw new TypeError("Generator is already executing.");
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
- if (y = 0, t) op = [op[0] & 2, t.value];
- switch (op[0]) {
- case 0: case 1: t = op; break;
- case 4: _.label++; return { value: op[1], done: false };
- case 5: _.label++; y = op[1]; op = [0]; continue;
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
- default:
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
- if (t[2]) _.ops.pop();
- _.trys.pop(); continue;
- }
- op = body.call(thisArg, _);
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
- }
-};
-import { users, snippets, collections, collectionItems, comments } from "@shared/schema";
-import { eq, and, or, isNotNull, ilike, sql, desc, asc } from "drizzle-orm";
-var MemStorage = /** @class */ (function () {
- function MemStorage() {
- this.users = new Map();
- this.snippets = new Map();
- this.collections = new Map();
- this.collectionItems = new Map();
- this.userIdCounter = 1;
- this.snippetIdCounter = 1;
- this.collectionIdCounter = 1;
- this.collectionItemIdCounter = 1;
- // Initialize with some sample data
- this.initializeSampleData();
- }
- MemStorage.prototype.initializeSampleData = function () {
- var _this = this;
- // Add sample snippets
- var sampleSnippets = [
- {
- title: "React useLocalStorage Hook",
- description: "Custom React hook to persist state in localStorage with type safety.",
- code: "import { useState, useEffect } from 'react';\n\nfunction useLocalStorage(\n key: string, \n initialValue: T\n): [T, (value: T) => void] {\n // Get stored value\n const readValue = (): T => {\n if (typeof window === 'undefined') {\n return initialValue;\n }\n try {\n const item = window.localStorage.getItem(key);\n return item ? JSON.parse(item) : initialValue;\n } catch (error) {\n console.warn('Error reading localStorage key', error);\n return initialValue;\n }\n };\n \n const [storedValue, setStoredValue] = useState(readValue);\n \n // Return a wrapped version of useState's setter\n const setValue = (value: T) => {\n try {\n // Save state\n setStoredValue(value);\n // Save to localStorage\n window.localStorage.setItem(key, JSON.stringify(value));\n } catch (error) {\n console.warn('Error setting localStorage key', error);\n }\n };\n\n useEffect(() => {\n setStoredValue(readValue());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [storedValue, setValue];\n}",
- language: "tsx",
- tags: ["react", "hooks", "typescript"],
- userId: null,
- isFavorite: false,
- viewCount: 12
- },
- {
- title: "Python Decorator for Timing",
- description: "A simple Python decorator to measure and log function execution time.",
- code: "import time\nimport functools\nimport logging\n\ndef timer(func):\n \"\"\"Print the runtime of the decorated function\"\"\"\n @functools.wraps(func)\n def wrapper_timer(*args, **kwargs):\n start_time = time.perf_counter()\n value = func(*args, **kwargs)\n end_time = time.perf_counter()\n run_time = end_time - start_time\n logging.info(f\"Completed {func.__name__!r} in {run_time:.4f} secs\")\n return value\n return wrapper_timer\n\n# Example usage\n@timer\ndef waste_some_time(num_times):\n for _ in range(num_times):\n sum([i**2 for i in range(10000)])\n \n# Call it\nwaste_some_time(100)",
- language: "python",
- tags: ["python", "decorators", "performance"],
- userId: null,
- isFavorite: false,
- viewCount: 24
- },
- {
- title: "CSS Grid Layout Template",
- description: "Responsive grid layout with areas for header, sidebar, content and footer.",
- code: ".grid-container {\n display: grid;\n grid-template-columns: repeat(12, 1fr);\n grid-template-rows: auto 1fr auto;\n grid-template-areas:\n \"h h h h h h h h h h h h\"\n \"s s c c c c c c c c c c\"\n \"f f f f f f f f f f f f\";\n min-height: 100vh;\n gap: 1rem;\n}\n\n.header { grid-area: h; }\n.sidebar { grid-area: s; }\n.content { grid-area: c; }\n.footer { grid-area: f; }\n\n/* Tablet layout */\n@media (max-width: 992px) {\n .grid-container {\n grid-template-areas:\n \"h h h h h h h h h h h h\"\n \"s s s s c c c c c c c c\"\n \"f f f f f f f f f f f f\";\n }\n}\n\n/* Mobile layout */\n@media (max-width: 768px) {\n .grid-container {\n grid-template-areas:\n \"h h h h h h h h h h h h\"\n \"c c c c c c c c c c c c\"\n \"s s s s s s s s s s s s\"\n \"f f f f f f f f f f f f\";\n }\n}",
- language: "css",
- tags: ["css", "grid", "responsive"],
- userId: null,
- isFavorite: true,
- viewCount: 41
- },
- {
- title: "JavaScript Array Methods Cheatsheet",
- description: "Quick reference for common JavaScript array methods with examples.",
- code: "/* Array methods cheatsheet */\n\n// ADDING ELEMENTS\narray.push(item); // Add to end\narray.unshift(item); // Add to beginning\narray.splice(index, 0, item); // Add at position\n\n// REMOVING ELEMENTS\narray.pop(); // Remove from end\narray.shift(); // Remove from beginning\narray.splice(index, 1); // Remove at position\n\n// TRANSFORMATION\narray.map(callback); // Create new array with results\narray.filter(callback); // Create array with elements that pass test\narray.reduce(callback, initialValue); // Reduce to single value\narray.sort(compareFunction); // Sort elements\narray.reverse(); // Reverse order\n\n// SEARCHING\narray.find(callback); // Find first matching element\narray.findIndex(callback); // Find index of first match\narray.includes(item); // Check if array contains item\narray.indexOf(item); // Find index of item (-1 if not found)\n\n// ITERATION\narray.forEach(callback); // Execute function on each element\n\n// JOINING & SPLITTING\narray.join(separator); // Join elements into string\nstring.split(separator); // Split string into array",
- language: "javascript",
- tags: ["javascript", "arrays", "cheatsheet"],
- userId: null,
- isFavorite: true,
- viewCount: 137
- },
- {
- title: "Tailwind Dark Mode Toggle",
- description: "React component for toggling dark mode with system preference detection.",
- code: "import { useState, useEffect } from 'react';\n\nconst DarkModeToggle = () => {\n const [darkMode, setDarkMode] = useState(false);\n\n useEffect(() => {\n // Check for system preference when component mounts\n const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;\n setDarkMode(\n localStorage.getItem('darkMode') !== null\n ? localStorage.getItem('darkMode') === 'true'\n : prefersDark\n );\n }, []);\n\n useEffect(() => {\n // Update document class when darkMode state changes\n if (darkMode) {\n document.documentElement.classList.add('dark');\n localStorage.setItem('darkMode', 'true');\n } else {\n document.documentElement.classList.remove('dark');\n localStorage.setItem('darkMode', 'false');\n }\n }, [darkMode]);\n\n return (\n setDarkMode(!darkMode)}\n className=\"p-2 rounded-md text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700\"\n aria-label={darkMode ? 'Switch to light mode' : 'Switch to dark mode'}\n >\n {darkMode ? (\n \n ) : (\n \n )}\n \n );\n};",
- language: "jsx",
- tags: ["react", "tailwind", "darkmode"],
- userId: null,
- isFavorite: false,
- viewCount: 52
- },
- {
- title: "Go Error Handling Pattern",
- description: "Best practices for handling errors in Go with custom error types.",
- code: "package main\n\nimport (\n \"errors\"\n \"fmt\"\n)\n\n// Define custom error types\ntype NotFoundError struct {\n ID string\n}\n\nfunc (e *NotFoundError) Error() string {\n return fmt.Sprintf(\"entity with ID %s not found\", e.ID)\n}\n\n// Function that returns different error types\nfunc GetUser(id string) (User, error) {\n // Simulate user not found\n if id == \"\" {\n return User{}, &NotFoundError{ID: id}\n }\n \n // Simulate another error\n if id == \"invalid\" {\n return User{}, errors.New(\"invalid user ID format\")\n }\n \n // Success\n return User{ID: id, Name: \"John Doe\"}, nil\n}\n\n// Error handling pattern with type checking\nfunc main() {\n user, err := GetUser(\"\")\n if err != nil {\n // Check specific error type\n if notFoundErr, ok := err.(*NotFoundError); ok {\n fmt.Printf(\"Could not find user: %v\\n\", notFoundErr)\n // Handle not found case\n } else {\n fmt.Printf(\"Error getting user: %v\\n\", err)\n // Handle other errors\n }\n return\n }\n \n // Process the user\n fmt.Printf(\"Found user: %s\\n\", user.Name)\n}",
- language: "go",
- tags: ["go", "error-handling", "best-practices"],
- userId: null,
- isFavorite: false,
- viewCount: 18
- }
- ];
- // Add sample collections
- var sampleCollections = [
- {
- name: "React Patterns",
- description: "Collection of useful React patterns and hooks",
- userId: null
- },
- {
- name: "CSS Layouts",
- description: "Responsive CSS layout techniques",
- userId: null
- },
- {
- name: "JavaScript Essentials",
- description: "Must-know JavaScript concepts and utilities",
- userId: null
- }
- ];
- // Add all sample snippets
- sampleSnippets.forEach(function (snippet) {
- _this.createSnippet(__assign(__assign({}, snippet), { viewCount: snippet.viewCount || 0, isFavorite: snippet.isFavorite || false }));
- });
- // Add all sample collections
- var collectionIds = sampleCollections.map(function (collection) {
- return _this.createCollection(collection).then(function (c) { return c.id; });
- });
- // Once all collections are created, add snippets to them
- Promise.all(collectionIds).then(function (ids) {
- // Add React useLocalStorage and Tailwind Dark Mode Toggle to React Patterns
- _this.addSnippetToCollection({ collectionId: ids[0], snippetId: 1 });
- _this.addSnippetToCollection({ collectionId: ids[0], snippetId: 5 });
- // Add CSS Grid Layout to CSS Layouts
- _this.addSnippetToCollection({ collectionId: ids[1], snippetId: 3 });
- // Add JavaScript Array Methods to JavaScript Essentials
- _this.addSnippetToCollection({ collectionId: ids[2], snippetId: 4 });
- });
- };
- // User operations
- MemStorage.prototype.getUser = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- return __generator(this, function (_a) {
- return [2 /*return*/, this.users.get(id)];
- });
- });
- };
- MemStorage.prototype.getUserByUsername = function (username) {
- return __awaiter(this, void 0, void 0, function () {
- return __generator(this, function (_a) {
- return [2 /*return*/, Array.from(this.users.values()).find(function (user) { return user.username === username; })];
- });
- });
- };
- MemStorage.prototype.createUser = function (insertUser) {
- return __awaiter(this, void 0, void 0, function () {
- var id, user;
- return __generator(this, function (_a) {
- id = this.userIdCounter++;
- user = __assign(__assign({}, insertUser), { id: id });
- this.users.set(id, user);
- return [2 /*return*/, user];
- });
- });
- };
- // Snippet operations
- MemStorage.prototype.getSnippets = function (filters) {
- return __awaiter(this, void 0, void 0, function () {
- var snippets, languages_1, langLower_1, tags_1, tagLower_1, searchTerm_1;
- return __generator(this, function (_a) {
- snippets = Array.from(this.snippets.values());
- if (filters) {
- // Filter by language - support both single language and multiple languages
- if (filters.language) {
- if (Array.isArray(filters.language)) {
- languages_1 = filters.language.map(function (lang) { return lang.toLowerCase(); });
- snippets = snippets.filter(function (s) {
- return s.language && languages_1.includes(s.language.toLowerCase());
- });
- }
- else {
- langLower_1 = filters.language.toLowerCase();
- snippets = snippets.filter(function (s) {
- return s.language && s.language.toLowerCase() === langLower_1;
- });
- }
- }
- // Filter by tag - support both single tag and multiple tags
- if (filters.tag) {
- if (Array.isArray(filters.tag)) {
- tags_1 = filters.tag.map(function (tag) { return tag.toLowerCase(); });
- snippets = snippets.filter(function (s) { var _a; return (_a = s.tags) === null || _a === void 0 ? void 0 : _a.some(function (tag) { return tags_1.includes(tag.toLowerCase()); }); });
- }
- else {
- tagLower_1 = filters.tag.toLowerCase();
- snippets = snippets.filter(function (s) { var _a; return (_a = s.tags) === null || _a === void 0 ? void 0 : _a.some(function (tag) { return tag.toLowerCase() === tagLower_1; }); });
- }
- }
- // Filter by favorites
- if (filters.favorites) {
- snippets = snippets.filter(function (s) { return s.isFavorite; });
- }
- // Filter by search term (title, description, code)
- if (filters.search) {
- searchTerm_1 = filters.search.toLowerCase();
- snippets = snippets.filter(function (s) {
- return s.title.toLowerCase().includes(searchTerm_1) ||
- (s.description && s.description.toLowerCase().includes(searchTerm_1)) ||
- (s.code && s.code.toLowerCase().includes(searchTerm_1));
- });
- }
- }
- // Sort by most recently updated
- return [2 /*return*/, snippets.sort(function (a, b) {
- var dateA = new Date(a.updatedAt).getTime();
- var dateB = new Date(b.updatedAt).getTime();
- return dateB - dateA;
- })];
- });
- });
- };
- MemStorage.prototype.getSnippet = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- return __generator(this, function (_a) {
- return [2 /*return*/, this.snippets.get(id)];
- });
- });
- };
- MemStorage.prototype.createSnippet = function (snippet) {
- return __awaiter(this, void 0, void 0, function () {
- var id, now, newSnippet;
- return __generator(this, function (_a) {
- id = this.snippetIdCounter++;
- now = new Date();
- newSnippet = __assign(__assign({}, snippet), { id: id, createdAt: now, updatedAt: now, viewCount: snippet.viewCount || 0, isFavorite: snippet.isFavorite || false });
- this.snippets.set(id, newSnippet);
- return [2 /*return*/, newSnippet];
- });
- });
- };
- MemStorage.prototype.updateSnippet = function (id, snippet) {
- return __awaiter(this, void 0, void 0, function () {
- var existingSnippet, updatedSnippet;
- return __generator(this, function (_a) {
- existingSnippet = this.snippets.get(id);
- if (!existingSnippet) {
- throw new Error("Snippet with id ".concat(id, " not found"));
- }
- updatedSnippet = __assign(__assign(__assign({}, existingSnippet), snippet), { updatedAt: new Date() });
- this.snippets.set(id, updatedSnippet);
- return [2 /*return*/, updatedSnippet];
- });
- });
- };
- MemStorage.prototype.deleteSnippet = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- var collectionItemsToDelete, _i, collectionItemsToDelete_1, item;
- return __generator(this, function (_a) {
- collectionItemsToDelete = Array.from(this.collectionItems.values())
- .filter(function (item) { return item.snippetId === id; });
- for (_i = 0, collectionItemsToDelete_1 = collectionItemsToDelete; _i < collectionItemsToDelete_1.length; _i++) {
- item = collectionItemsToDelete_1[_i];
- this.collectionItems.delete(item.id);
- }
- // Delete the snippet
- this.snippets.delete(id);
- return [2 /*return*/];
- });
- });
- };
- MemStorage.prototype.incrementSnippetViewCount = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- var snippet, updatedSnippet;
- return __generator(this, function (_a) {
- snippet = this.snippets.get(id);
- if (snippet) {
- updatedSnippet = __assign(__assign({}, snippet), { viewCount: snippet.viewCount + 1 });
- this.snippets.set(id, updatedSnippet);
- }
- return [2 /*return*/];
- });
- });
- };
- MemStorage.prototype.toggleSnippetFavorite = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- var snippet, updatedSnippet;
- return __generator(this, function (_a) {
- snippet = this.snippets.get(id);
- if (!snippet) {
- throw new Error("Snippet with id ".concat(id, " not found"));
- }
- updatedSnippet = __assign(__assign({}, snippet), { isFavorite: !snippet.isFavorite });
- this.snippets.set(id, updatedSnippet);
- return [2 /*return*/, updatedSnippet];
- });
- });
- };
- // Language and tag operations
- MemStorage.prototype.getLanguages = function () {
- return __awaiter(this, void 0, void 0, function () {
- var languages, _i, _a, snippet;
- return __generator(this, function (_b) {
- languages = new Set();
- for (_i = 0, _a = this.snippets.values(); _i < _a.length; _i++) {
- snippet = _a[_i];
- languages.add(snippet.language);
- }
- return [2 /*return*/, Array.from(languages).sort()];
- });
- });
- };
- MemStorage.prototype.getTags = function () {
- return __awaiter(this, void 0, void 0, function () {
- var tags, _i, _a, snippet, _b, _c, tag;
- return __generator(this, function (_d) {
- tags = new Set();
- for (_i = 0, _a = this.snippets.values(); _i < _a.length; _i++) {
- snippet = _a[_i];
- if (snippet.tags) {
- for (_b = 0, _c = snippet.tags; _b < _c.length; _b++) {
- tag = _c[_b];
- tags.add(tag);
- }
- }
- }
- return [2 /*return*/, Array.from(tags).sort()];
- });
- });
- };
- // Collection operations
- MemStorage.prototype.getCollections = function () {
- return __awaiter(this, void 0, void 0, function () {
- return __generator(this, function (_a) {
- return [2 /*return*/, Array.from(this.collections.values())];
- });
- });
- };
- MemStorage.prototype.getCollection = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- return __generator(this, function (_a) {
- return [2 /*return*/, this.collections.get(id)];
- });
- });
- };
- MemStorage.prototype.createCollection = function (collection) {
- return __awaiter(this, void 0, void 0, function () {
- var id, now, newCollection;
- return __generator(this, function (_a) {
- id = this.collectionIdCounter++;
- now = new Date();
- newCollection = __assign(__assign({}, collection), { id: id, createdAt: now, updatedAt: now });
- this.collections.set(id, newCollection);
- return [2 /*return*/, newCollection];
- });
- });
- };
- MemStorage.prototype.updateCollection = function (id, collection) {
- return __awaiter(this, void 0, void 0, function () {
- var existingCollection, updatedCollection;
- return __generator(this, function (_a) {
- existingCollection = this.collections.get(id);
- if (!existingCollection) {
- throw new Error("Collection with id ".concat(id, " not found"));
- }
- updatedCollection = __assign(__assign(__assign({}, existingCollection), collection), { updatedAt: new Date() });
- this.collections.set(id, updatedCollection);
- return [2 /*return*/, updatedCollection];
- });
- });
- };
- MemStorage.prototype.deleteCollection = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- var collectionItemsToDelete, _i, collectionItemsToDelete_2, item;
- return __generator(this, function (_a) {
- collectionItemsToDelete = Array.from(this.collectionItems.values())
- .filter(function (item) { return item.collectionId === id; });
- for (_i = 0, collectionItemsToDelete_2 = collectionItemsToDelete; _i < collectionItemsToDelete_2.length; _i++) {
- item = collectionItemsToDelete_2[_i];
- this.collectionItems.delete(item.id);
- }
- // Delete the collection
- this.collections.delete(id);
- return [2 /*return*/];
- });
- });
- };
- // Collection items operations
- MemStorage.prototype.getCollectionSnippets = function (collectionId) {
- return __awaiter(this, void 0, void 0, function () {
- var items, snippets, _i, items_1, item, snippet;
- return __generator(this, function (_a) {
- items = Array.from(this.collectionItems.values())
- .filter(function (item) { return item.collectionId === collectionId; });
- snippets = [];
- for (_i = 0, items_1 = items; _i < items_1.length; _i++) {
- item = items_1[_i];
- snippet = this.snippets.get(item.snippetId);
- if (snippet) {
- snippets.push(snippet);
- }
- }
- return [2 /*return*/, snippets];
- });
- });
- };
- MemStorage.prototype.addSnippetToCollection = function (collectionItem) {
- return __awaiter(this, void 0, void 0, function () {
- var snippet, collection, existingItem, id, now, newItem;
- return __generator(this, function (_a) {
- snippet = this.snippets.get(collectionItem.snippetId);
- collection = this.collections.get(collectionItem.collectionId);
- if (!snippet) {
- throw new Error("Snippet with id ".concat(collectionItem.snippetId, " not found"));
- }
- if (!collection) {
- throw new Error("Collection with id ".concat(collectionItem.collectionId, " not found"));
- }
- existingItem = Array.from(this.collectionItems.values()).find(function (item) { return item.collectionId === collectionItem.collectionId &&
- item.snippetId === collectionItem.snippetId; });
- if (existingItem) {
- return [2 /*return*/, existingItem];
- }
- id = this.collectionItemIdCounter++;
- now = new Date();
- newItem = __assign(__assign({}, collectionItem), { id: id, createdAt: now });
- this.collectionItems.set(id, newItem);
- return [2 /*return*/, newItem];
- });
- });
- };
- MemStorage.prototype.removeSnippetFromCollection = function (collectionId, snippetId) {
- return __awaiter(this, void 0, void 0, function () {
- var item;
- return __generator(this, function (_a) {
- item = Array.from(this.collectionItems.values()).find(function (item) { return item.collectionId === collectionId && item.snippetId === snippetId; });
- if (item) {
- this.collectionItems.delete(item.id);
- }
- return [2 /*return*/];
- });
- });
- };
- return MemStorage;
-}());
-export { MemStorage };
-var DatabaseStorage = /** @class */ (function () {
- function DatabaseStorage() {
- }
- // Sharing operations
- DatabaseStorage.prototype.getSnippetByShareId = function (shareId) {
- return __awaiter(this, void 0, void 0, function () {
- var db, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db.select().from(snippets).where(eq(snippets.shareId, shareId)).limit(1)];
- case 2:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.generateShareId = function (snippetId) {
- return __awaiter(this, void 0, void 0, function () {
- var db, shareId;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- shareId = Math.random().toString(36).substring(2, 10);
- // Update the snippet with the new share ID
- return [4 /*yield*/, db
- .update(snippets)
- .set({ shareId: shareId })
- .where(eq(snippets.id, snippetId))];
- case 2:
- // Update the snippet with the new share ID
- _a.sent();
- return [2 /*return*/, shareId];
- }
- });
- });
- };
- DatabaseStorage.prototype.toggleSnippetPublic = function (snippetId) {
- return __awaiter(this, void 0, void 0, function () {
- var db, currentSnippet, isPublic, shareId, updatedSnippet;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db
- .select()
- .from(snippets)
- .where(eq(snippets.id, snippetId))];
- case 2:
- currentSnippet = (_a.sent())[0];
- if (!currentSnippet) {
- throw new Error("Snippet with ID ".concat(snippetId, " not found"));
- }
- isPublic = !currentSnippet.isPublic;
- shareId = currentSnippet.shareId;
- if (isPublic && !shareId) {
- shareId = Math.random().toString(36).substring(2, 10);
- }
- return [4 /*yield*/, db
- .update(snippets)
- .set({ isPublic: isPublic, shareId: shareId })
- .where(eq(snippets.id, snippetId))
- .returning()];
- case 3:
- updatedSnippet = (_a.sent())[0];
- return [2 /*return*/, updatedSnippet];
- }
- });
- });
- };
- // Comment operations
- DatabaseStorage.prototype.getCommentsBySnippetId = function (snippetId) {
- return __awaiter(this, void 0, void 0, function () {
- var db;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [2 /*return*/, db
- .select()
- .from(comments)
- .where(eq(comments.snippetId, snippetId))
- .orderBy(asc(comments.createdAt))];
- }
- });
- });
- };
- DatabaseStorage.prototype.createComment = function (comment) {
- return __awaiter(this, void 0, void 0, function () {
- var db, newComment;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db
- .insert(comments)
- .values(comment)
- .returning()];
- case 2:
- newComment = (_a.sent())[0];
- return [2 /*return*/, newComment];
- }
- });
- });
- };
- DatabaseStorage.prototype.updateComment = function (id, comment) {
- return __awaiter(this, void 0, void 0, function () {
- var db, updatedComment;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db
- .update(comments)
- .set(__assign(__assign({}, comment), { updatedAt: new Date() }))
- .where(eq(comments.id, id))
- .returning()];
- case 2:
- updatedComment = (_a.sent())[0];
- if (!updatedComment) {
- throw new Error("Comment with ID ".concat(id, " not found"));
- }
- return [2 /*return*/, updatedComment];
- }
- });
- });
- };
- DatabaseStorage.prototype.deleteComment = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- var db;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db
- .delete(comments)
- .where(eq(comments.id, id))];
- case 2:
- _a.sent();
- return [2 /*return*/];
- }
- });
- });
- };
- DatabaseStorage.prototype.getUser = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- var db, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db.select().from(users).where(eq(users.id, id)).limit(1)];
- case 2:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.getUserByEmail = function (email) {
- return __awaiter(this, void 0, void 0, function () {
- var db, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db.select().from(users).where(eq(users.email, email)).limit(1)];
- case 2:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.createUser = function (insertUser) {
- return __awaiter(this, void 0, void 0, function () {
- var db, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db.insert(users).values(insertUser).returning()];
- case 2:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.upsertUser = function (insertUser) {
- return __awaiter(this, void 0, void 0, function () {
- var db, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db
- .insert(users)
- .values(insertUser)
- .onConflictDoUpdate({
- target: users.id,
- set: __assign(__assign({}, insertUser), { updatedAt: new Date() })
- })
- .returning()];
- case 2:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.getSnippets = function (filters) {
- return __awaiter(this, void 0, void 0, function () {
- var db, query, languageConditions, tagArray;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- query = db.select().from(snippets);
- if (filters) {
- // Handle language filter (single string or array)
- if (filters.language) {
- if (Array.isArray(filters.language)) {
- languageConditions = filters.language.map(function (lang) {
- return eq(snippets.language, lang);
- });
- if (languageConditions.length > 0) {
- query = query.where(or.apply(void 0, languageConditions));
- }
- }
- else {
- // Single language
- query = query.where(eq(snippets.language, filters.language));
- }
- }
- // Handle search filter
- if (filters.search) {
- query = query.where(or(ilike(snippets.title, "%".concat(filters.search, "%")), ilike(snippets.description || '', "%".concat(filters.search, "%")), ilike(snippets.code, "%".concat(filters.search, "%"))));
- }
- // Handle tag filter (single string or array)
- if (filters.tag) {
- if (Array.isArray(filters.tag)) {
- tagArray = filters.tag.map(function (t) { return t.toString(); });
- query = query.where(sql(templateObject_1 || (templateObject_1 = __makeTemplateObject(["", " && ARRAY[", "]::text[]"], ["", " && ARRAY[", "]::text[]"])), snippets.tags, tagArray));
- }
- else {
- // Single tag using contains operator
- query = query.where(sql(templateObject_2 || (templateObject_2 = __makeTemplateObject(["", " @> ARRAY[", "]::text[]"], ["", " @> ARRAY[", "]::text[]"])), snippets.tags, filters.tag));
- }
- }
- // Handle favorites filter
- if (filters.favorites) {
- query = query.where(eq(snippets.isFavorite, true));
- }
- }
- // Order by most recently updated
- query = query.orderBy(desc(snippets.updatedAt));
- return [4 /*yield*/, query];
- case 2: return [2 /*return*/, _a.sent()];
- }
- });
- });
- };
- DatabaseStorage.prototype.getSnippet = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- var db, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db.select().from(snippets).where(eq(snippets.id, id)).limit(1)];
- case 2:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.createSnippet = function (snippet) {
- return __awaiter(this, void 0, void 0, function () {
- var db, now, snippetToInsert, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- now = new Date();
- snippetToInsert = {
- title: snippet.title,
- code: snippet.code,
- language: snippet.language,
- description: snippet.description || null,
- tags: snippet.tags || null,
- userId: snippet.userId || null,
- isFavorite: snippet.isFavorite || false,
- // These fields are handled automatically by defaults
- // createdAt and updatedAt are set by defaultNow()
- // viewCount is set by default(0)
- };
- return [4 /*yield*/, db.insert(snippets).values(snippetToInsert).returning()];
- case 2:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.updateSnippet = function (id, snippet) {
- return __awaiter(this, void 0, void 0, function () {
- var db, existingSnippet, updateData, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, this.getSnippet(id)];
- case 2:
- existingSnippet = _a.sent();
- if (!existingSnippet) {
- throw new Error("Snippet with id ".concat(id, " not found"));
- }
- updateData = {
- title: snippet.title,
- code: snippet.code,
- language: snippet.language,
- description: snippet.description || null,
- tags: snippet.tags || null,
- userId: snippet.userId || null,
- isFavorite: snippet.isFavorite !== undefined ? snippet.isFavorite : existingSnippet.isFavorite,
- updatedAt: new Date()
- };
- return [4 /*yield*/, db
- .update(snippets)
- .set(updateData)
- .where(eq(snippets.id, id))
- .returning()];
- case 3:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.deleteSnippet = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- var db, existingSnippet;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, this.getSnippet(id)];
- case 2:
- existingSnippet = _a.sent();
- if (!existingSnippet) {
- throw new Error("Snippet with id ".concat(id, " not found"));
- }
- // First delete all collection items that reference this snippet
- return [4 /*yield*/, db
- .delete(collectionItems)
- .where(eq(collectionItems.snippetId, id))];
- case 3:
- // First delete all collection items that reference this snippet
- _a.sent();
- // Then delete the snippet
- return [4 /*yield*/, db
- .delete(snippets)
- .where(eq(snippets.id, id))];
- case 4:
- // Then delete the snippet
- _a.sent();
- return [2 /*return*/];
- }
- });
- });
- };
- DatabaseStorage.prototype.incrementSnippetViewCount = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- var db, existingSnippet;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, this.getSnippet(id)];
- case 2:
- existingSnippet = _a.sent();
- if (!existingSnippet) {
- throw new Error("Snippet with id ".concat(id, " not found"));
- }
- return [4 /*yield*/, db
- .update(snippets)
- .set({
- viewCount: sql(templateObject_3 || (templateObject_3 = __makeTemplateObject(["", " + 1"], ["", " + 1"])), snippets.viewCount)
- })
- .where(eq(snippets.id, id))];
- case 3:
- _a.sent();
- return [2 /*return*/];
- }
- });
- });
- };
- DatabaseStorage.prototype.toggleSnippetFavorite = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- var db, existingSnippet, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, this.getSnippet(id)];
- case 2:
- existingSnippet = _a.sent();
- if (!existingSnippet) {
- throw new Error("Snippet with id ".concat(id, " not found"));
- }
- return [4 /*yield*/, db
- .update(snippets)
- .set({
- isFavorite: !existingSnippet.isFavorite
- })
- .where(eq(snippets.id, id))
- .returning()];
- case 3:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.getLanguages = function () {
- return __awaiter(this, void 0, void 0, function () {
- var db, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db
- .selectDistinct({ language: snippets.language })
- .from(snippets)];
- case 2:
- result = _a.sent();
- return [2 /*return*/, result.map(function (r) { return r.language; })];
- }
- });
- });
- };
- DatabaseStorage.prototype.getTags = function () {
- return __awaiter(this, void 0, void 0, function () {
- var db, allTags, uniqueTags;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db
- .select({ tags: snippets.tags })
- .from(snippets)
- .where(isNotNull(snippets.tags))];
- case 2:
- allTags = _a.sent();
- uniqueTags = new Set();
- allTags.forEach(function (row) {
- if (row.tags) {
- row.tags.forEach(function (tag) { return uniqueTags.add(tag); });
- }
- });
- return [2 /*return*/, Array.from(uniqueTags)];
- }
- });
- });
- };
- DatabaseStorage.prototype.getCollections = function () {
- return __awaiter(this, void 0, void 0, function () {
- var db;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db.select().from(collections)];
- case 2: return [2 /*return*/, _a.sent()];
- }
- });
- });
- };
- DatabaseStorage.prototype.getCollection = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- var db, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db.select().from(collections).where(eq(collections.id, id)).limit(1)];
- case 2:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.createCollection = function (collection) {
- return __awaiter(this, void 0, void 0, function () {
- var db, now, collectionWithDefaults, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- now = new Date();
- collectionWithDefaults = __assign(__assign({}, collection), { createdAt: now, updatedAt: now });
- return [4 /*yield*/, db.insert(collections).values(collectionWithDefaults).returning()];
- case 2:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.updateCollection = function (id, collection) {
- return __awaiter(this, void 0, void 0, function () {
- var db, existingCollection, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, this.getCollection(id)];
- case 2:
- existingCollection = _a.sent();
- if (!existingCollection) {
- throw new Error("Collection with id ".concat(id, " not found"));
- }
- return [4 /*yield*/, db
- .update(collections)
- .set(__assign(__assign({}, collection), { updatedAt: new Date() }))
- .where(eq(collections.id, id))
- .returning()];
- case 3:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.deleteCollection = function (id) {
- return __awaiter(this, void 0, void 0, function () {
- var db, existingCollection;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, this.getCollection(id)];
- case 2:
- existingCollection = _a.sent();
- if (!existingCollection) {
- throw new Error("Collection with id ".concat(id, " not found"));
- }
- // First delete all collection items that reference this collection
- return [4 /*yield*/, db
- .delete(collectionItems)
- .where(eq(collectionItems.collectionId, id))];
- case 3:
- // First delete all collection items that reference this collection
- _a.sent();
- // Then delete the collection
- return [4 /*yield*/, db
- .delete(collections)
- .where(eq(collections.id, id))];
- case 4:
- // Then delete the collection
- _a.sent();
- return [2 /*return*/];
- }
- });
- });
- };
- DatabaseStorage.prototype.getCollectionSnippets = function (collectionId) {
- return __awaiter(this, void 0, void 0, function () {
- var db, existingCollection, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, this.getCollection(collectionId)];
- case 2:
- existingCollection = _a.sent();
- if (!existingCollection) {
- throw new Error("Collection with id ".concat(collectionId, " not found"));
- }
- return [4 /*yield*/, db
- .select()
- .from(snippets)
- .innerJoin(collectionItems, eq(snippets.id, collectionItems.snippetId))
- .where(eq(collectionItems.collectionId, collectionId))];
- case 3:
- result = _a.sent();
- return [2 /*return*/, result.map(function (row) { return (__assign({}, row.snippets)); })];
- }
- });
- });
- };
- DatabaseStorage.prototype.addSnippetToCollection = function (collectionItem) {
- return __awaiter(this, void 0, void 0, function () {
- var db, existingCollection, existingSnippet, existing, now, itemWithDefaults, result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, this.getCollection(collectionItem.collectionId)];
- case 2:
- existingCollection = _a.sent();
- if (!existingCollection) {
- throw new Error("Collection with id ".concat(collectionItem.collectionId, " not found"));
- }
- return [4 /*yield*/, this.getSnippet(collectionItem.snippetId)];
- case 3:
- existingSnippet = _a.sent();
- if (!existingSnippet) {
- throw new Error("Snippet with id ".concat(collectionItem.snippetId, " not found"));
- }
- return [4 /*yield*/, db
- .select()
- .from(collectionItems)
- .where(and(eq(collectionItems.collectionId, collectionItem.collectionId), eq(collectionItems.snippetId, collectionItem.snippetId)))
- .limit(1)];
- case 4:
- existing = _a.sent();
- if (existing.length > 0) {
- throw new Error("Snippet is already in the collection");
- }
- now = new Date();
- itemWithDefaults = __assign(__assign({}, collectionItem), { createdAt: now });
- return [4 /*yield*/, db
- .insert(collectionItems)
- .values(itemWithDefaults)
- .returning()];
- case 5:
- result = _a.sent();
- return [2 /*return*/, result[0]];
- }
- });
- });
- };
- DatabaseStorage.prototype.removeSnippetFromCollection = function (collectionId, snippetId) {
- return __awaiter(this, void 0, void 0, function () {
- var db, existing;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, import('./db')];
- case 1:
- db = (_a.sent()).db;
- return [4 /*yield*/, db
- .select()
- .from(collectionItems)
- .where(and(eq(collectionItems.collectionId, collectionId), eq(collectionItems.snippetId, snippetId)))
- .limit(1)];
- case 2:
- existing = _a.sent();
- if (existing.length === 0) {
- throw new Error("Snippet is not in the collection");
- }
- return [4 /*yield*/, db
- .delete(collectionItems)
- .where(and(eq(collectionItems.collectionId, collectionId), eq(collectionItems.snippetId, snippetId)))];
- case 3:
- _a.sent();
- return [2 /*return*/];
- }
- });
- });
- };
- return DatabaseStorage;
-}());
-export { DatabaseStorage };
-// Create an instance of DatabaseStorage to use throughout the application
-// Using in-memory storage for now until database issues are resolved
-// Switch to database storage for persistent data
-export var storage = new DatabaseStorage();
-var templateObject_1, templateObject_2, templateObject_3;
diff --git a/server/test-db.js b/server/test-db.js
deleted file mode 100644
index 29e911e..0000000
--- a/server/test-db.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import 'dotenv/config';
-import pg from 'pg';
-const { Pool } = pg;
-
-const connectionString = process.env.DATABASE_URL || 'postgresql://codepatchwork_user:1S1HwpTVdmilD8tNeGmI@localhost:5432/codepatchwork';
-console.log("Using connection string:", connectionString.replace(/:[^:]*@/, ':***@')); // Hide password in logs
-
-const pool = new Pool({ connectionString });
-
-async function testDatabase() {
- console.log("Testing database connection...");
- let client;
-
- try {
- client = await pool.connect();
- console.log("β
Successfully connected to database!");
-
- // Basic query test
- const timeResult = await client.query('SELECT NOW() as time');
- console.log("β
Database time:", timeResult.rows[0].time);
-
- // List tables
- const tablesResult = await client.query(`
- SELECT table_name
- FROM information_schema.tables
- WHERE table_schema = 'public'
- `);
-
- console.log("\nTables in database:");
- if (tablesResult.rows.length === 0) {
- console.log("β No tables found in public schema!");
- } else {
- tablesResult.rows.forEach(row => {
- console.log(`- ${row.table_name}`);
- });
- }
-
- // Try queries on specific tables
- try {
- const snippetsResult = await client.query('SELECT COUNT(*) FROM snippets');
- console.log(`β
Found ${snippetsResult.rows[0].count} records in snippets table`);
- } catch (e) {
- console.log(`β Error querying snippets table: ${e.message}`);
- }
-
- try {
- const tagsResult = await client.query('SELECT COUNT(*) FROM tags');
- console.log(`β
Found ${tagsResult.rows[0].count} records in tags table`);
- } catch (e) {
- console.log(`β Error querying tags table: ${e.message}`);
- }
-
- try {
- const collectionsResult = await client.query('SELECT COUNT(*) FROM collections');
- console.log(`β
Found ${collectionsResult.rows[0].count} records in collections table`);
- } catch (e) {
- console.log(`β Error querying collections table: ${e.message}`);
- }
-
- // Check users table
- try {
- const usersResult = await client.query('SELECT COUNT(*) FROM users');
- console.log(`β
Found ${usersResult.rows[0].count} records in users table`);
-
- if (usersResult.rows[0].count > 0) {
- // Check column types for the first user
- const userColumns = await client.query('SELECT * FROM users LIMIT 1');
- console.log("\nUser table structure:");
- Object.entries(userColumns.rows[0]).forEach(([key, value]) => {
- console.log(`- ${key}: ${typeof value} (${value === null ? 'null' : value})`);
- });
- }
- } catch (e) {
- console.log(`β Error querying users table: ${e.message}`);
- }
-
- } catch (connectionError) {
- console.error("β Failed to connect to database:", connectionError);
- } finally {
- if (client) client.release();
- await pool.end();
- }
-}
-
-// Run the test
-testDatabase();
diff --git a/server/winston-test.ts b/server/winston-test.ts
new file mode 100644
index 0000000..c6f69e7
--- /dev/null
+++ b/server/winston-test.ts
@@ -0,0 +1,6 @@
+// winston-test.ts
+import logger from './logger';
+
+logger.info("β
Winston basic test: info level");
+logger.warn("β οΈ Winston basic test: warn level");
+logger.error("β Winston basic test: error level");
diff --git a/test_db_connection.js b/test_db_connection.js
deleted file mode 100644
index 831ca57..0000000
--- a/test_db_connection.js
+++ /dev/null
@@ -1,34 +0,0 @@
-const { Pool } = require('pg');
-
-// Use the same connection string as your application
-const pool = new Pool({
- connectionString: 'postgresql://codepatchwork_user:1S1HwpTVdmilD8tNeGmI@localhost:5432/codepatchwork'
-});
-
-async function testConnection() {
- try {
- // Test basic connection
- const client = await pool.connect();
- console.log('Successfully connected to the database');
-
- // Test querying the snippets table
- const snippetsResult = await client.query('SELECT COUNT(*) FROM snippets');
- console.log('Snippets count:', snippetsResult.rows[0].count);
-
- // Test querying the tags table
- const tagsResult = await client.query('SELECT COUNT(*) FROM tags');
- console.log('Tags count:', tagsResult.rows[0].count);
-
- // Test querying the collections table
- const collectionsResult = await client.query('SELECT COUNT(*) FROM collections');
- console.log('Collections count:', collectionsResult.rows[0].count);
-
- client.release();
- } catch (error) {
- console.error('Database connection error:', error);
- } finally {
- pool.end();
- }
-}
-
-testConnection();
diff --git a/test_db_lowercase.js b/test_db_lowercase.js
deleted file mode 100644
index a2dd4d9..0000000
--- a/test_db_lowercase.js
+++ /dev/null
@@ -1,46 +0,0 @@
-// Direct database test with lowercase column names
-import pg from 'pg';
-const { Pool } = pg;
-
-const pool = new Pool({
- connectionString: 'postgres://codepatchwork_user:1S1HwpTVdmilD8tNeGmI@localhost:5432/codepatchwork'
-});
-
-async function testLowercaseQueries() {
- const client = await pool.connect();
- try {
- console.log('Connected to database successfully');
-
- // Test snippets table with lowercase column names
- console.log('Testing snippets table:');
- const snippetsResult = await client.query('SELECT COUNT(*) FROM snippets');
- console.log(`- Found ${snippetsResult.rows[0].count} snippets`);
-
- const snippetsSample = await client.query('SELECT id, title, language, userid, createdat, updatedat FROM snippets LIMIT 3');
- console.log('- Sample snippets:', JSON.stringify(snippetsSample.rows, null, 2));
-
- // Test collectionItems table
- console.log('\nTesting collectionItems table:');
- try {
- const collectionItemsResult = await client.query('SELECT COUNT(*) FROM "collectionItems"');
- console.log(`- Found ${collectionItemsResult.rows[0].count} collection items`);
-
- const columnInfo = await client.query(`
- SELECT column_name, data_type
- FROM information_schema.columns
- WHERE table_name = 'collectionItems'
- `);
- console.log('- Column information:', columnInfo.rows);
- } catch (err) {
- console.error('- Error testing collectionItems:', err.message);
- }
-
- } catch (err) {
- console.error('Database test error:', err);
- } finally {
- client.release();
- await pool.end();
- }
-}
-
-testLowercaseQueries();
diff --git a/test_db_simple.js b/test_db_simple.js
deleted file mode 100644
index 62f3a59..0000000
--- a/test_db_simple.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import dotenv from 'dotenv';
-import pg from 'pg';
-
-dotenv.config();
-const { Pool } = pg;
-
-console.log('DATABASE_URL from env:', process.env.DATABASE_URL);
-
-const pool = new Pool({
- connectionString: process.env.DATABASE_URL
-});
-
-async function test() {
- try {
- const client = await pool.connect();
- console.log('Successfully connected to the database!');
- const result = await client.query('SELECT COUNT(*) FROM snippets');
- console.log('Snippets count:', result.rows[0].count);
- client.release();
- } catch (error) {
- console.error('Error connecting to database:', error);
- } finally {
- await pool.end();
- }
-}
-
-test();
diff --git a/test_dump.sql b/test_dump.sql
deleted file mode 100644
index 3d08e6c..0000000
--- a/test_dump.sql
+++ /dev/null
@@ -1,595 +0,0 @@
---
--- PostgreSQL database dump
---
-
--- Dumped from database version 17.5 (Ubuntu 17.5-0ubuntu0.25.04.1)
--- Dumped by pg_dump version 17.5 (Ubuntu 17.5-0ubuntu0.25.04.1)
-
-SET statement_timeout = 0;
-SET lock_timeout = 0;
-SET idle_in_transaction_session_timeout = 0;
-SET transaction_timeout = 0;
-SET client_encoding = 'UTF8';
-SET standard_conforming_strings = on;
-SELECT pg_catalog.set_config('search_path', '', false);
-SET check_function_bodies = false;
-SET xmloption = content;
-SET client_min_messages = warning;
-SET row_security = off;
-
-SET default_tablespace = '';
-
-SET default_table_access_method = heap;
-
---
--- Name: collectionItems; Type: TABLE; Schema: public; Owner: codepatchwork_user
---
-
-CREATE TABLE public."collectionItems" (
- id integer NOT NULL,
- collectionid integer NOT NULL,
- snippetid integer NOT NULL,
- createdat timestamp with time zone DEFAULT CURRENT_TIMESTAMP
-);
-
-
-ALTER TABLE public."collectionItems" OWNER TO codepatchwork_user;
-
---
--- Name: collectionItems_id_seq; Type: SEQUENCE; Schema: public; Owner: codepatchwork_user
---
-
-CREATE SEQUENCE public."collectionItems_id_seq"
- AS integer
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-
-ALTER SEQUENCE public."collectionItems_id_seq" OWNER TO codepatchwork_user;
-
---
--- Name: collectionItems_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: codepatchwork_user
---
-
-ALTER SEQUENCE public."collectionItems_id_seq" OWNED BY public."collectionItems".id;
-
-
---
--- Name: collections; Type: TABLE; Schema: public; Owner: codepatchwork_user
---
-
-CREATE TABLE public.collections (
- id integer NOT NULL,
- name character varying NOT NULL,
- description text,
- userid character varying,
- createdat timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
- updatedat timestamp with time zone DEFAULT CURRENT_TIMESTAMP
-);
-
-
-ALTER TABLE public.collections OWNER TO codepatchwork_user;
-
---
--- Name: collections_id_seq; Type: SEQUENCE; Schema: public; Owner: codepatchwork_user
---
-
-CREATE SEQUENCE public.collections_id_seq
- AS integer
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-
-ALTER SEQUENCE public.collections_id_seq OWNER TO codepatchwork_user;
-
---
--- Name: collections_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: codepatchwork_user
---
-
-ALTER SEQUENCE public.collections_id_seq OWNED BY public.collections.id;
-
-
---
--- Name: comments; Type: TABLE; Schema: public; Owner: codepatchwork_user
---
-
-CREATE TABLE public.comments (
- id integer NOT NULL,
- snippetid integer NOT NULL,
- userid character varying,
- content text NOT NULL,
- createdat timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
- updatedat timestamp with time zone DEFAULT CURRENT_TIMESTAMP
-);
-
-
-ALTER TABLE public.comments OWNER TO codepatchwork_user;
-
---
--- Name: comments_id_seq; Type: SEQUENCE; Schema: public; Owner: codepatchwork_user
---
-
-CREATE SEQUENCE public.comments_id_seq
- AS integer
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-
-ALTER SEQUENCE public.comments_id_seq OWNER TO codepatchwork_user;
-
---
--- Name: comments_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: codepatchwork_user
---
-
-ALTER SEQUENCE public.comments_id_seq OWNED BY public.comments.id;
-
-
---
--- Name: sessions; Type: TABLE; Schema: public; Owner: codepatchwork_user
---
-
-CREATE TABLE public.sessions (
- sid character varying NOT NULL,
- sess jsonb NOT NULL,
- expire timestamp without time zone NOT NULL
-);
-
-
-ALTER TABLE public.sessions OWNER TO codepatchwork_user;
-
---
--- Name: snippets; Type: TABLE; Schema: public; Owner: codepatchwork_user
---
-
-CREATE TABLE public.snippets (
- id integer NOT NULL,
- title character varying NOT NULL,
- description text,
- code text NOT NULL,
- language character varying,
- tags text[],
- userid character varying,
- createdat timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
- updatedat timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
- viewcount integer DEFAULT 0,
- isfavorite boolean DEFAULT false,
- shareid character varying,
- ispublic boolean DEFAULT false
-);
-
-
-ALTER TABLE public.snippets OWNER TO codepatchwork_user;
-
---
--- Name: snippets_id_seq; Type: SEQUENCE; Schema: public; Owner: codepatchwork_user
---
-
-CREATE SEQUENCE public.snippets_id_seq
- AS integer
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-
-ALTER SEQUENCE public.snippets_id_seq OWNER TO codepatchwork_user;
-
---
--- Name: snippets_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: codepatchwork_user
---
-
-ALTER SEQUENCE public.snippets_id_seq OWNED BY public.snippets.id;
-
-
---
--- Name: tags; Type: TABLE; Schema: public; Owner: codepatchwork_user
---
-
-CREATE TABLE public.tags (
- id integer NOT NULL,
- name character varying NOT NULL,
- count integer DEFAULT 1
-);
-
-
-ALTER TABLE public.tags OWNER TO codepatchwork_user;
-
---
--- Name: tags_id_seq; Type: SEQUENCE; Schema: public; Owner: codepatchwork_user
---
-
-CREATE SEQUENCE public.tags_id_seq
- AS integer
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-
-ALTER SEQUENCE public.tags_id_seq OWNER TO codepatchwork_user;
-
---
--- Name: tags_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: codepatchwork_user
---
-
-ALTER SEQUENCE public.tags_id_seq OWNED BY public.tags.id;
-
-
---
--- Name: users; Type: TABLE; Schema: public; Owner: codepatchwork_user
---
-
-CREATE TABLE public.users (
- id text NOT NULL,
- email character varying,
- display_name character varying,
- photo_url character varying,
- created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
- updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP
-);
-
-
-ALTER TABLE public.users OWNER TO codepatchwork_user;
-
---
--- Name: collectionItems id; Type: DEFAULT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public."collectionItems" ALTER COLUMN id SET DEFAULT nextval('public."collectionItems_id_seq"'::regclass);
-
-
---
--- Name: collections id; Type: DEFAULT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public.collections ALTER COLUMN id SET DEFAULT nextval('public.collections_id_seq'::regclass);
-
-
---
--- Name: comments id; Type: DEFAULT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public.comments ALTER COLUMN id SET DEFAULT nextval('public.comments_id_seq'::regclass);
-
-
---
--- Name: snippets id; Type: DEFAULT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public.snippets ALTER COLUMN id SET DEFAULT nextval('public.snippets_id_seq'::regclass);
-
-
---
--- Name: tags id; Type: DEFAULT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public.tags ALTER COLUMN id SET DEFAULT nextval('public.tags_id_seq'::regclass);
-
-
---
--- Data for Name: collectionItems; Type: TABLE DATA; Schema: public; Owner: codepatchwork_user
---
-
-COPY public."collectionItems" (id, collectionid, snippetid, createdat) FROM stdin;
-\.
-
-
---
--- Data for Name: collections; Type: TABLE DATA; Schema: public; Owner: codepatchwork_user
---
-
-COPY public.collections (id, name, description, userid, createdat, updatedat) FROM stdin;
-\.
-
-
---
--- Data for Name: comments; Type: TABLE DATA; Schema: public; Owner: codepatchwork_user
---
-
-COPY public.comments (id, snippetid, userid, content, createdat, updatedat) FROM stdin;
-\.
-
-
---
--- Data for Name: sessions; Type: TABLE DATA; Schema: public; Owner: codepatchwork_user
---
-
-COPY public.sessions (sid, sess, expire) FROM stdin;
-\.
-
-
---
--- Data for Name: snippets; Type: TABLE DATA; Schema: public; Owner: codepatchwork_user
---
-
-COPY public.snippets (id, title, description, code, language, tags, userid, createdat, updatedat, viewcount, isfavorite, shareid, ispublic) FROM stdin;
-1000 Test Direct Insert Testing direct database insertion console.log("Hello from direct insert"); javascript {test,direct-insert} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-21 08:32:36.923858+08 2025-05-21 08:32:36.923858+08 0 f \N f
-1001 Auto ID Test Testing auto increment after sequence reset console.log("Testing auto ID assignment"); javascript {test,sequence} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-21 08:34:12.519156+08 2025-05-21 08:34:12.519156+08 0 f \N f
-1039 Delete File Deletes a file from the file system. File f = new File("delete_me.txt");\nf.delete(); java {file,delete,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.283129+08 2025-05-23 07:59:25.283129+08 0 f \N f
-1040 Platform-Independent Path Separator Constructs a path string using the OS-specific separator. String path = "folder" + File.separator + "file.txt"; java {file,separator,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.284554+08 2025-05-23 07:59:25.284554+08 0 f \N f
-1041 Read Binary File Reads a binary file byte-by-byte using FileInputStream. FileInputStream fis = new FileInputStream("binary.dat");\nint b;\nwhile ((b = fis.read()) != -1) {\n System.out.print((char) b);\n}\nfis.close(); java {filestream,binary,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.285917+08 2025-05-23 07:59:25.285917+08 0 f \N f
-1005 Java Deep Clone Example Illustrates how to perform a deep clone of an object using the Cloneable interface class Address implements Cloneable {\n String city;\n\n Address(String city) {\n this.city = city;\n }\n\n @Override\n protected Object clone() throws CloneNotSupportedException {\n return new Address(this.city);\n }\n}\n\nclass Person implements Cloneable {\n String name;\n Address address;\n\n Person(String name, Address address) {\n this.name = name;\n this.address = address;\n }\n\n @Override\n protected Object clone() throws CloneNotSupportedException {\n Person cloned = (Person) super.clone();\n cloned.address = (Address) address.clone(); // Deep copy\n return cloned;\n }\n\n public static void main(String[] args) throws CloneNotSupportedException {\n Address addr = new Address("Berlin");\n Person original = new Person("Alice", addr);\n Person clone = (Person) original.clone();\n\n clone.address.city = "Munich";\n\n System.out.println("Original city: " + original.address.city);\n System.out.println("Cloned city: " + clone.address.city);\n }\n} java {java,clone,deepcopy,object,oop} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-21 15:39:34.60282+08 2025-05-21 15:39:34.60282+08 0 f \N f
-1006 Java Stream API Filter and Map Example showing how to filter a list and transform its elements using Java Stream API. import java.util.Arrays;\\nimport java.util.List;\\nimport java.util.stream.Collectors;\\n\\npublic class StreamExample {\\n public static void main(String[] args) {\\n List names = Arrays.asList(\\"John\\", \\"Jane\\", \\"Adam\\", \\"Alice\\", \\"Bob\\");\\n \\n // Filter names starting with 'A' and convert to uppercase\\n List filteredNames = names.stream()\\n .filter(name -> name.startsWith(\\"A\\"))\\n .map(String::toUpperCase)\\n .collect(Collectors.toList());\\n \\n // Print the result\\n System.out.println(\\"Names starting with 'A': \\" + filteredNames);\\n }\\n} java {java,streams,functional} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-21 17:13:44.708085+08 2025-05-21 17:13:44.708085+08 0 f \N f
-1042 Write Binary File Writes bytes to a binary file using FileOutputStream. FileOutputStream fos = new FileOutputStream("output.bin");\nfos.write(new byte[]{65, 66, 67});\nfos.close(); java {filestream,binary,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.287262+08 2025-05-23 07:59:25.287262+08 0 f \N f
-1012 Using HashSet Shows how duplicates are not allowed in a HashSet. HashSet set = new HashSet<>();\nset.add(1);\nset.add(2);\nset.add(1);\nSystem.out.println(set); java {hashset,sets,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.209795+08 2025-05-23 07:57:33.209795+08 0 f \N f
-1013 TreeSet Sorted Order Inserts values in a TreeSet to show automatic sorting. TreeSet ts = new TreeSet<>();\nts.add("banana");\nts.add("apple");\nSystem.out.println(ts); java {treeset,sets,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.211336+08 2025-05-23 07:57:33.211336+08 0 f \N f
-1043 Buffered Writer Example Writes a line using BufferedWriter to optimize IO. BufferedWriter bw = new BufferedWriter(new FileWriter("buffered.txt"));\nbw.write("Buffered line");\nbw.newLine();\nbw.close(); java {file,bufferedwriter,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.288681+08 2025-05-23 07:59:25.288681+08 0 f \N f
-1044 Buffered Reader Example Reads a single line using BufferedReader. BufferedReader br = new BufferedReader(new FileReader("buffered.txt"));\nSystem.out.println(br.readLine());\nbr.close(); java {file,bufferedreader,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.290035+08 2025-05-23 07:59:25.290035+08 0 f \N f
-1045 Compress File with GZIP Compresses a string to a .gz file using GZIPOutputStream. GZIPOutputStream gos = new GZIPOutputStream(new FileOutputStream("file.gz"));\ngos.write("Hello".getBytes());\ngos.close(); java {gzip,compress,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.291382+08 2025-05-23 07:59:25.291382+08 0 f \N f
-1046 Decompress GZIP File Reads and decompresses a .gz file using GZIPInputStream. GZIPInputStream gis = new GZIPInputStream(new FileInputStream("file.gz"));\nint data;\nwhile ((data = gis.read()) != -1) {\n System.out.print((char) data);\n}\ngis.close(); java {gzip,decompress,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.292737+08 2025-05-23 07:59:25.292737+08 0 f \N f
-1047 Try-with-Resources for File Handling Uses try-with-resources for automatic closing of streams. try (BufferedReader br = new BufferedReader(new FileReader("sample.txt"))) {\n System.out.println(br.readLine());\n} catch (IOException e) {\n e.printStackTrace();\n} java {try,resources,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.294059+08 2025-05-23 08:00:18.490287+08 0 t \N f
-1074 Replace Characters Replaces underscores with spaces in a string. String cleaned = "Hello_World".replace('_', ' '); java {string,replace,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.425456+08 2025-05-23 08:16:44.425456+08 0 f \N f
-1075 Compare Strings (Case-Insensitive) Compares two strings ignoring case. System.out.println("hello".equalsIgnoreCase("HELLO")); java {string,compare,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.426905+08 2025-05-23 08:16:44.426905+08 0 f \N f
-1076 Split CSV Line Splits a CSV line into an array of values. String line = ""Apple","Banana","Cherry"";\nString[] fruits = line.replaceAll("\\"", "").split(","); java {string,split,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.428456+08 2025-05-23 08:16:44.428456+08 0 f \N f
-1 React useLocalStorage Hook Custom React hook to persist state in localStorage with type safety. import { useState, useEffect } from 'react';\\n\\nfunction useLocalStorage(\\n key: string, \\n initialValue: T\\n): [T, (value: T) => void] {\\n // Get stored value\\n const readValue = (): T => {\\n if (typeof window === 'undefined') {\\n return initialValue;\\n }\\n try {\\n const item = window.localStorage.getItem(key);\\n return item ? JSON.parse(item) : initialValue;\\n } catch (error) {\\n console.warn('Error reading localStorage key', error);\\n return initialValue;\\n }\\n };\\n \\n const [storedValue, setStoredValue] = useState(readValue);\\n \\n // Return a wrapped version of useState's setter\\n const setValue = (value: T) => {\\n try {\\n // Save state\\n setStoredValue(value);\\n // Save to localStorage\\n window.localStorage.setItem(key, JSON.stringify(value));\\n } catch (error) {\\n console.warn('Error setting localStorage key', error);\\n }\\n };\\n\\n useEffect(() => {\\n setStoredValue(readValue());\\n // eslint-disable-next-line react-hooks/exhaustive-deps\\n }, []);\\n\\n return [storedValue, setValue];\\n} tsx {react,hooks,typescript} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-16 16:51:32.268+08 2025-05-16 16:51:32.268+08 12 f \N f
-2 Python Decorator for Timing A simple Python decorator to measure and log function execution time. import time\\nimport functools\\nimport logging\\n\\ndef timer(func):\\n \\"\\"\\"Print the runtime of the decorated function\\"\\"\\"\\n @functools.wraps(func)\\n def wrapper_timer(*args, **kwargs):\\n start_time = time.perf_counter()\\n value = func(*args, **kwargs)\\n end_time = time.perf_counter()\\n run_time = end_time - start_time\\n logging.info(f\\"Completed {func.__name__!r} in {run_time:.4f} secs\\")\\n return value\\n return wrapper_timer\\n\\n# Example usage\\n@timer\\ndef waste_some_time(num_times):\\n for _ in range(num_times):\\n sum([i**2 for i in range(10000)])\\n \\n# Call it\\nwaste_some_time(100) python {python,decorators,performance} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-16 16:51:32.268+08 2025-05-16 16:51:32.268+08 24 f \N f
-3 CSS Grid Layout Template Responsive grid layout with areas for header, sidebar, content and footer. .grid-container {\\n display: grid;\\n grid-template-columns: repeat(12, 1fr);\\n grid-template-rows: auto 1fr auto;\\n grid-template-areas:\\n \\"h h h h h h h h h h h h\\"\\n \\"s s c c c c c c c c c c\\"\\n \\"f f f f f f f f f f f f\\";\\n min-height: 100vh;\\n gap: 1rem;\\n}\\n\\n.header { grid-area: h; }\\n.sidebar { grid-area: s; }\\n.content { grid-area: c; }\\n.footer { grid-area: f; }\\n\\n/* Tablet layout */\\n@media (max-width: 992px) {\\n .grid-container {\\n grid-template-areas:\\n \\"h h h h h h h h h h h h\\"\\n \\"s s s s c c c c c c c c\\"\\n \\"f f f f f f f f f f f f\\";\\n }\\n}\\n\\n/* Mobile layout */\\n@media (max-width: 768px) {\\n .grid-container {\\n grid-template-areas:\\n \\"h h h h h h h h h h h h\\"\\n \\"c c c c c c c c c c c c\\"\\n \\"s s s s s s s s s s s s\\"\\n \\"f f f f f f f f f f f f\\";\\n }\\n} css {css,grid,responsive} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-16 16:51:32.268+08 2025-05-16 16:51:32.268+08 41 t \N f
-1008 Create and Access Array Initializes a simple integer array and accesses the second element. int[] numbers = {10, 20, 30};\nSystem.out.println(numbers[1]); java {arrays,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.201856+08 2025-05-23 07:57:33.201856+08 0 f \N f
-1009 Iterate Array with For Loop Uses a standard for loop to iterate through an array. for (int i = 0; i < numbers.length; i++) {\n System.out.println(numbers[i]);\n} java {arrays,loops,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.20457+08 2025-05-23 07:57:33.20457+08 0 f \N f
-1010 ArrayList Basic Operations Creates an ArrayList, adds elements, and accesses the first item. ArrayList list = new ArrayList<>();\nlist.add("Apple");\nlist.add("Banana");\nSystem.out.println(list.get(0)); java {arraylist,lists,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.20612+08 2025-05-23 07:57:33.20612+08 0 f \N f
-1011 LinkedList as List Demonstrates a LinkedList used as a list. LinkedList cities = new LinkedList<>();\ncities.add("Berlin");\ncities.add("Munich"); java {linkedlist,lists,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.208232+08 2025-05-23 07:57:33.208232+08 0 f \N f
-1014 HashMap Key-Value Store Stores key-value pairs and retrieves a value using a key. HashMap scores = new HashMap<>();\nscores.put("Alice", 90);\nscores.put("Bob", 80);\nSystem.out.println(scores.get("Alice")); java {hashmap,maps,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.212849+08 2025-05-23 07:57:33.212849+08 0 f \N f
-1015 TreeMap Sorted Keys TreeMap automatically sorts keys. TreeMap map = new TreeMap<>();\nmap.put(2, "B");\nmap.put(1, "A");\nSystem.out.println(map); java {treemap,maps,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.214931+08 2025-05-23 07:57:33.214931+08 0 f \N f
-1016 Stack Push and Pop Simulates a stack with push and pop using ArrayDeque. Deque stack = new ArrayDeque<>();\nstack.push(10);\nstack.push(20);\nSystem.out.println(stack.pop()); java {stack,deque,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.216362+08 2025-05-23 07:57:33.216362+08 0 f \N f
-1017 Queue Operations Shows basic FIFO queue behavior using LinkedList. Queue queue = new LinkedList<>();\nqueue.add("first");\nqueue.add("second");\nSystem.out.println(queue.remove()); java {queue,linkedlist,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.219071+08 2025-05-23 07:57:33.219071+08 0 f \N f
-1018 Iterator Example Demonstrates use of an Iterator on a List. Iterator it = list.iterator();\nwhile(it.hasNext()) {\n System.out.println(it.next());\n} java {iterator,collections,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.220773+08 2025-05-23 07:57:33.220773+08 0 f \N f
-1019 Extended For Loop Uses enhanced for loop to iterate over a collection. for(String item : list) {\n System.out.println(item);\n} java {for-each,loop,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.222278+08 2025-05-23 07:57:33.222278+08 0 f \N f
-1020 Set Operations Performs set intersection using retainAll. Set set1 = new HashSet<>(List.of("a", "b"));\nSet set2 = new HashSet<>(List.of("b", "c"));\nset1.retainAll(set2);\nSystem.out.println(set1); java {sets,hashset,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.223735+08 2025-05-23 07:57:33.223735+08 0 f \N f
-1021 Map Entry Iteration Iterates through a mapβs key-value pairs. for (Map.Entry entry : scores.entrySet()) {\n System.out.println(entry.getKey() + ": " + entry.getValue());\n} java {map,iteration,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.225182+08 2025-05-23 07:57:33.225182+08 0 f \N f
-1022 Multi-dimensional Array Accesses an element from a 2D array. int[][] matrix = {{1, 2}, {3, 4}};\nSystem.out.println(matrix[1][0]); java {array,2d,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.226667+08 2025-05-23 07:57:33.226667+08 0 f \N f
-1023 Check if Collection is Empty Checks whether a collection is empty using isEmpty(). if (list.isEmpty()) {\n System.out.println("List is empty");\n} java {collections,check,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.22807+08 2025-05-23 07:57:33.22807+08 0 f \N f
-1024 Map Default Value Uses getOrDefault to avoid null for missing keys. int value = scores.getOrDefault("Carol", 0);\nSystem.out.println(value); java {map,default,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.229462+08 2025-05-23 07:57:33.229462+08 0 f \N f
-1025 Deque as Queue Uses offer and poll to simulate queue operations with Deque. Deque deque = new ArrayDeque<>();\ndeque.offer("first");\ndeque.offer("second");\nSystem.out.println(deque.poll()); java {queue,deque,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.231046+08 2025-05-23 07:57:33.231046+08 0 f \N f
-1026 Remove from List by Index Removes an element at index 1 from a list. list.remove(1); java {list,remove,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.232745+08 2025-05-23 07:57:33.232745+08 0 f \N f
-1027 Check Set Contains Element Checks if a set contains a specific element. System.out.println(set.contains("banana")); java {set,contains,unit4} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:57:33.234356+08 2025-05-23 07:57:33.234356+08 0 f \N f
-1028 Check File Exists Checks if the file 'data.txt' exists in the current directory. File file = new File("data.txt");\nSystem.out.println(file.exists()); java {file,check,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.264632+08 2025-05-23 07:59:25.264632+08 0 f \N f
-1029 Create New Directory Creates a single new directory named 'mydir'. File dir = new File("mydir");\ndir.mkdir(); java {file,directory,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.266643+08 2025-05-23 07:59:25.266643+08 0 f \N f
-1030 Create Nested Directories Creates a nested directory structure. File dirs = new File("a/b/c");\ndirs.mkdirs(); java {file,mkdirs,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.26815+08 2025-05-23 07:59:25.26815+08 0 f \N f
-1031 List Files in Directory Lists all files and folders in the current directory. File folder = new File(".");\nFile[] files = folder.listFiles();\nfor (File f : files) {\n System.out.println(f.getName());\n} java {file,list,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.269618+08 2025-05-23 07:59:25.269618+08 0 f \N f
-1032 Rename or Move File Renames or moves a file using renameTo(). File oldFile = new File("old.txt");\nFile newFile = new File("new.txt");\noldFile.renameTo(newFile); java {file,rename,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.271116+08 2025-05-23 07:59:25.271116+08 0 f \N f
-1033 Write to File Writes a string to a file using FileWriter. FileWriter writer = new FileWriter("output.txt");\nwriter.write("Hello, file!");\nwriter.close(); java {filewriter,write,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.27261+08 2025-05-23 07:59:25.27261+08 0 f \N f
-1034 Read Text File Line-by-Line Reads each line of a text file using BufferedReader. BufferedReader reader = new BufferedReader(new FileReader("input.txt"));\nString line;\nwhile ((line = reader.readLine()) != null) {\n System.out.println(line);\n}\nreader.close(); java {filereader,read,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.275448+08 2025-05-23 07:59:25.275448+08 0 f \N f
-1035 Copy File Content Copies text from one file to another line-by-line. BufferedReader in = new BufferedReader(new FileReader("input.txt"));\nBufferedWriter out = new BufferedWriter(new FileWriter("copy.txt"));\nString line;\nwhile ((line = in.readLine()) != null) {\n out.write(line);\n out.newLine();\n}\nin.close();\nout.close(); java {file,copy,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.277375+08 2025-05-23 07:59:25.277375+08 0 f \N f
-1036 Check if File is Directory Checks whether the file object points to a directory. File f = new File("mydir");\nSystem.out.println(f.isDirectory()); java {file,isDirectory,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.278943+08 2025-05-23 07:59:25.278943+08 0 f \N f
-1037 Get File Size Returns the size of a file in bytes. File f = new File("example.txt");\nSystem.out.println(f.length() + " bytes"); java {file,size,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.280375+08 2025-05-23 07:59:25.280375+08 0 f \N f
-1038 Check File Permissions Checks if a file is readable and writable. File f = new File("test.txt");\nSystem.out.println(f.canRead());\nSystem.out.println(f.canWrite()); java {file,permissions,unit6} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 07:59:25.281767+08 2025-05-23 07:59:25.281767+08 0 f \N f
-1048 Override toString() Method Overrides the toString() method to customize object output. public class Book {\n String title = "Java";\n public String toString() {\n return "Book Title: " + title;\n }\n} java {object,tostring,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.321338+08 2025-05-23 08:16:03.321338+08 0 f \N f
-1049 Use toString() Implicitly Demonstrates implicit toString() call when printing an object. Book b = new Book();\nSystem.out.println(b); java {object,tostring,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.324601+08 2025-05-23 08:16:03.324601+08 0 f \N f
-1050 Compare with == (Identity) Compares object references with == (should return false). String a = new String("Hello");\nString b = new String("Hello");\nSystem.out.println(a == b); java {object,compare,identity,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.326059+08 2025-05-23 08:16:03.326059+08 0 f \N f
-1051 Compare with equals() Compares object content using equals() (should return true). System.out.println(a.equals(b)); java {object,equals,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.327431+08 2025-05-23 08:16:03.327431+08 0 f \N f
-1052 Override equals() Method Custom equals method comparing content. public boolean equals(Object obj) {\n if (this == obj) return true;\n if (!(obj instanceof Book)) return false;\n Book b = (Book) obj;\n return this.title.equals(b.title);\n} java {object,equals,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.328735+08 2025-05-23 08:16:03.328735+08 0 f \N f
-1053 Override hashCode() Method Generates hashCode aligned with equals(). public int hashCode() {\n return title.hashCode();\n} java {object,hashcode,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.330068+08 2025-05-23 08:16:03.330068+08 0 f \N f
-1054 Compare Objects with hashCode() Prints hash codes to check object identity via content. System.out.println(a.hashCode());\nSystem.out.println(b.hashCode()); java {object,hashcode,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.331346+08 2025-05-23 08:16:03.331346+08 0 f \N f
-1055 Use compareTo() for Ordering Implements compareTo for sorting Book objects alphabetically. public int compareTo(Book other) {\n return this.title.compareTo(other.title);\n} java {object,compareto,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.332647+08 2025-05-23 08:16:03.332647+08 0 f \N f
-1056 CompareTo Return Values Returns negative, zero, or positive based on order. System.out.println(b1.compareTo(b2)); java {object,compareto,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.333934+08 2025-05-23 08:16:03.333934+08 0 f \N f
-1057 Implement Comparable Interface Implements Comparable for ordering custom objects. public class Book implements Comparable {\n String title;\n public int compareTo(Book b) {\n return this.title.compareTo(b.title);\n }\n} java {comparable,compareto,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.335189+08 2025-05-23 08:16:03.335189+08 0 f \N f
-1058 Call clone() with Cloneable Implements clone() with Cloneable interface. public class Book implements Cloneable {\n public Object clone() throws CloneNotSupportedException {\n return super.clone();\n }\n} java {object,clone,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.336523+08 2025-05-23 08:16:03.336523+08 0 f \N f
-1059 Create Shallow Copy Clones an object using the overridden clone() method. Book b2 = (Book) b1.clone(); java {object,clone,shallow,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.337782+08 2025-05-23 08:16:03.337782+08 0 f \N f
-1060 Check equals Reflexive Verifies reflexivity of equals() method. System.out.println(b1.equals(b1)); // should be true java {equals,reflexive,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.339087+08 2025-05-23 08:16:03.339087+08 0 f \N f
-1061 Check equals Symmetric Tests symmetry of equals() implementation. System.out.println(b1.equals(b2));\nSystem.out.println(b2.equals(b1)); java {equals,symmetric,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.340391+08 2025-05-23 08:16:03.340391+08 0 f \N f
-1062 Check equals Transitive Tests transitivity of equals() for three objects. System.out.println(b1.equals(b2) && b2.equals(b3) && b1.equals(b3)); java {equals,transitive,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.341683+08 2025-05-23 08:16:03.341683+08 0 f \N f
-1063 Check equals with null Ensures equals returns false when compared to null. System.out.println(b1.equals(null)); // should be false java {equals,"null",unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.343124+08 2025-05-23 08:16:03.343124+08 0 f \N f
-1064 Call toString() Explicitly Calls toString() explicitly for formatted string output. System.out.println(b.toString()); java {object,tostring,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.344644+08 2025-05-23 08:16:03.344644+08 0 f \N f
-1065 Custom toString() Format Formats output with custom brackets. public String toString() {\n return "[Title: " + title + "]";\n} java {tostring,custom,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.346062+08 2025-05-23 08:16:03.346062+08 0 f \N f
-1066 Deep Copy Hint Comment hint for implementing deep copy instead of shallow. // Deep copy requires cloning referenced objects manually. java {clone,deep,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.347527+08 2025-05-23 08:16:03.347527+08 0 f \N f
-1067 Compare Wrapper Objects Shows behavior of == with wrapper class caching. Integer a = 100;\nInteger b = 100;\nSystem.out.println(a == b); // true due to caching java {compare,wrapper,unit2} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:03.349164+08 2025-05-23 08:16:03.349164+08 0 f \N f
-1068 String Literal Declaration Declares and initializes a string using literal syntax. String greeting = "Hello, Java!"; java {string,literal,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.416772+08 2025-05-23 08:16:44.416772+08 0 f \N f
-1069 Concatenate Strings Joins multiple strings using the + operator. String fullName = "John" + " " + "Doe"; java {string,concatenation,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.418712+08 2025-05-23 08:16:44.418712+08 0 f \N f
-1070 Convert int to String Converts an integer to a string using valueOf(). String numberStr = String.valueOf(123); java {string,conversion,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.420083+08 2025-05-23 08:16:44.420083+08 0 f \N f
-1071 Convert String to int Parses an integer value from a string. int number = Integer.parseInt("123"); java {string,parsing,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.421445+08 2025-05-23 08:16:44.421445+08 0 f \N f
-1072 Check String Contains Checks if a substring exists within a string. System.out.println("Java Programming".contains("gram")); java {string,contains,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.422808+08 2025-05-23 08:16:44.422808+08 0 f \N f
-1073 Get Substring Extracts a substring from the string. String name = "Johnathan";\nSystem.out.println(name.substring(0, 4)); java {string,substring,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.42409+08 2025-05-23 08:16:44.42409+08 0 f \N f
-1077 StringBuffer Append Appends strings using StringBuffer for performance. StringBuffer sb = new StringBuffer();\nsb.append("Hello");\nsb.append(" World");\nSystem.out.println(sb.toString()); java {stringbuffer,append,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.42982+08 2025-05-23 08:16:44.42982+08 0 f \N f
-1078 Trim Whitespace Removes leading and trailing whitespace from a string. String input = " padded ";\nSystem.out.println(input.trim()); java {string,trim,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.431378+08 2025-05-23 08:16:44.431378+08 0 f \N f
-1079 Create Current Date Creates a Date object representing the current moment. Date now = new Date();\nSystem.out.println(now); java {date,current,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.432939+08 2025-05-23 08:16:44.432939+08 0 f \N f
-1080 Format Date Formats the current date in German style. SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");\nString formatted = sdf.format(new Date());\nSystem.out.println(formatted); java {date,format,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.434376+08 2025-05-23 08:16:44.434376+08 0 f \N f
-1081 Parse Date from String Parses a date string into a Date object. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");\nDate d = sdf.parse("2025-05-23"); java {date,parse,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.435832+08 2025-05-23 08:16:44.435832+08 0 f \N f
-1082 Check Date Order Compares two dates to check chronological order. Date earlier = sdf.parse("2020-01-01");\nDate later = new Date();\nSystem.out.println(earlier.before(later)); java {date,compare,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.437176+08 2025-05-23 08:16:44.437176+08 0 f \N f
-1083 Use GregorianCalendar Creates a GregorianCalendar for a specific date. Calendar cal = new GregorianCalendar(2025, Calendar.MAY, 23);\nSystem.out.println(cal.getTime()); java {calendar,gregorian,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.438526+08 2025-05-23 08:16:44.438526+08 0 f \N f
-1084 Roll Calendar Date Rolls the calendar forward by one month. cal.roll(Calendar.MONTH, 1);\nSystem.out.println(cal.getTime()); java {calendar,roll,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.440895+08 2025-05-23 08:16:44.440895+08 0 f \N f
-1085 Get Calendar Field Extracts the year from a Calendar instance. int year = cal.get(Calendar.YEAR);\nSystem.out.println("Year: " + year); java {calendar,get,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.442459+08 2025-05-23 08:16:44.442459+08 0 f \N f
-1086 Set Calendar Field Sets a specific day in a Calendar object. cal.set(Calendar.DAY_OF_MONTH, 15);\nSystem.out.println(cal.getTime()); java {calendar,set,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.443856+08 2025-05-23 08:16:44.443856+08 0 f \N f
-1087 Format with DateFormat.SHORT Formats a date using a predefined short format. DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);\nSystem.out.println(df.format(new Date())); java {dateformat,short,unit5} rSH4fsZZboZhREcZjnwjOTkxit02 2025-05-23 08:16:44.445158+08 2025-05-23 08:16:44.445158+08 0 f \N f
-\.
-
-
---
--- Data for Name: tags; Type: TABLE DATA; Schema: public; Owner: codepatchwork_user
---
-
-COPY public.tags (id, name, count) FROM stdin;
-1 typescript 1
-2 css 1
-3 python 1
-4 hooks 1
-5 responsive 1
-6 grid 1
-7 decorators 1
-8 performance 1
-9 react 1
-\.
-
-
---
--- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: codepatchwork_user
---
-
-COPY public.users (id, email, display_name, photo_url, created_at, updated_at) FROM stdin;
-T2VGNpXA0CTxChHJbFBp7GfhYUl2 zkai8790@gmail.com εΌ΅ζ·ζ© https://lh3.googleusercontent.com/a/ACg8ocJ5myoc01U_tdg7cSutam1CBmfZPGL3jVz_rMQI20RZcSOFZxr9=s96-c 2025-05-20 07:50:53.611165+08 2025-05-20 19:29:38.979+08
-U0vTskMoAfextb67hjU3yVdpx273 randomork3@gmail.com Random Ork https://lh3.googleusercontent.com/a/ACg8ocI2sqIbZiMTq80e2WH1HLFzvHzsq4VQ064pYgE5pMDNouHlf3W3rw=s96-c 2025-05-20 21:18:35.391743+08 2025-05-20 22:20:26.667+08
-rSH4fsZZboZhREcZjnwjOTkxit02 hex316aa@gmail.com Erling Wulf Weinreich https://lh3.googleusercontent.com/a/ACg8ocItsHJMo2sJnkzRkAgfPJvr5lvnfAv-rrC7Yx8B187u2eLsElCO=s96-c 2025-05-20 07:26:10.504186+08 2025-05-23 08:30:50.995+08
-IfA9Mk31L2anZKqVFesYUmWwD9s2 hexawulf@gmail.com Erling Wulf Weinreich https://lh3.googleusercontent.com/a/ACg8ocICU2s0uzUhp110YqFEYWxzzo8BktSem8KsrMp6OMmMzrt8ciy_=s96-c 2025-05-20 08:30:16.370983+08 2025-05-22 16:44:04.342+08
-\.
-
-
---
--- Name: collectionItems_id_seq; Type: SEQUENCE SET; Schema: public; Owner: codepatchwork_user
---
-
-SELECT pg_catalog.setval('public."collectionItems_id_seq"', 1, false);
-
-
---
--- Name: collections_id_seq; Type: SEQUENCE SET; Schema: public; Owner: codepatchwork_user
---
-
-SELECT pg_catalog.setval('public.collections_id_seq', 1, false);
-
-
---
--- Name: comments_id_seq; Type: SEQUENCE SET; Schema: public; Owner: codepatchwork_user
---
-
-SELECT pg_catalog.setval('public.comments_id_seq', 1, false);
-
-
---
--- Name: snippets_id_seq; Type: SEQUENCE SET; Schema: public; Owner: codepatchwork_user
---
-
-SELECT pg_catalog.setval('public.snippets_id_seq', 1087, true);
-
-
---
--- Name: tags_id_seq; Type: SEQUENCE SET; Schema: public; Owner: codepatchwork_user
---
-
-SELECT pg_catalog.setval('public.tags_id_seq', 9, true);
-
-
---
--- Name: collectionItems collectionItems_collectionid_snippetid_key; Type: CONSTRAINT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public."collectionItems"
- ADD CONSTRAINT "collectionItems_collectionid_snippetid_key" UNIQUE (collectionid, snippetid);
-
-
---
--- Name: collectionItems collectionItems_pkey; Type: CONSTRAINT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public."collectionItems"
- ADD CONSTRAINT "collectionItems_pkey" PRIMARY KEY (id);
-
-
---
--- Name: collections collections_pkey; Type: CONSTRAINT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public.collections
- ADD CONSTRAINT collections_pkey PRIMARY KEY (id);
-
-
---
--- Name: comments comments_pkey; Type: CONSTRAINT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public.comments
- ADD CONSTRAINT comments_pkey PRIMARY KEY (id);
-
-
---
--- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public.sessions
- ADD CONSTRAINT sessions_pkey PRIMARY KEY (sid);
-
-
---
--- Name: snippets snippets_pkey; Type: CONSTRAINT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public.snippets
- ADD CONSTRAINT snippets_pkey PRIMARY KEY (id);
-
-
---
--- Name: tags tags_name_key; Type: CONSTRAINT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public.tags
- ADD CONSTRAINT tags_name_key UNIQUE (name);
-
-
---
--- Name: tags tags_pkey; Type: CONSTRAINT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public.tags
- ADD CONSTRAINT tags_pkey PRIMARY KEY (id);
-
-
---
--- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: codepatchwork_user
---
-
-ALTER TABLE ONLY public.users
- ADD CONSTRAINT users_pkey PRIMARY KEY (id);
-
-
---
--- Name: idx_collectionitems_collectionid; Type: INDEX; Schema: public; Owner: codepatchwork_user
---
-
-CREATE INDEX idx_collectionitems_collectionid ON public."collectionItems" USING btree (collectionid);
-
-
---
--- Name: idx_collectionitems_snippetid; Type: INDEX; Schema: public; Owner: codepatchwork_user
---
-
-CREATE INDEX idx_collectionitems_snippetid ON public."collectionItems" USING btree (snippetid);
-
-
---
--- Name: idx_comments_snippetid; Type: INDEX; Schema: public; Owner: codepatchwork_user
---
-
-CREATE INDEX idx_comments_snippetid ON public.comments USING btree (snippetid);
-
-
---
--- Name: idx_session_expire; Type: INDEX; Schema: public; Owner: codepatchwork_user
---
-
-CREATE INDEX idx_session_expire ON public.sessions USING btree (expire);
-
-
---
--- Name: SCHEMA public; Type: ACL; Schema: -; Owner: pg_database_owner
---
-
-GRANT ALL ON SCHEMA public TO codepatchwork_user;
-
-
---
--- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: codepatchwork_user
---
-
-ALTER DEFAULT PRIVILEGES FOR ROLE codepatchwork_user IN SCHEMA public GRANT ALL ON SEQUENCES TO codepatchwork_user;
-
-
---
--- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: codepatchwork_user
---
-
-ALTER DEFAULT PRIVILEGES FOR ROLE codepatchwork_user IN SCHEMA public GRANT ALL ON TABLES TO codepatchwork_user;
-
-
---
--- PostgreSQL database dump complete
---
-
diff --git a/vite.config.ts b/vite.config.ts
index 0ea964d..8ca86a5 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -44,6 +44,7 @@ export default defineConfig(async () => {
outDir: path.resolve(import.meta.dirname, "dist/public"),
emptyOutDir: true,
},
+ publicDir: path.resolve(import.meta.dirname, "public"),
/* ----------------------------------------------------------
* Env handling