So many changes!

This commit is contained in:
Donavon McDowell 2024-12-11 16:42:06 -07:00
parent 08b58ee729
commit 1de113913b
12 changed files with 65 additions and 38 deletions

View File

@ -0,0 +1 @@
MONGODB_URI='mongodb://localhost:27017'

View File

@ -3,12 +3,17 @@ const express = require('express');
const { MongoClient, ObjectId } = require('mongodb'); const { MongoClient, ObjectId } = require('mongodb');
const cors = require('cors'); const cors = require('cors');
const app = express(); const app = express();
require('dotenv').config();
const port = process.env.PORT || 3000; const port = process.env.PORT || 3000;
const mongodb_uri = process.env.MONGODB_URI || 'mongodb://localhost:27017'; const mongodb_uri = process.env.MONGODB_URI || 'mongodb://localhost:27017';
const mongodb_name = process.env.MONGODB_NAME || 'docucenter'; const mongodb_name = process.env.MONGODB_NAME || 'docucenter';
if(process.env.MONGODB_URI){
console.log("Using ENV: " + process.env.MONGODB_URI);
}
// Parse JSON bodies // Parse JSON bodies
app.use(express.json({ limit: '50mb' })); // For JSON data app.use(express.json({ limit: '50mb' })); // For JSON data
app.use(express.urlencoded({ limit: '50mb', extended: true })); // For URL-encoded data app.use(express.urlencoded({ limit: '50mb', extended: true })); // For URL-encoded data
@ -112,14 +117,14 @@ app.get('/documents/:id', async (req, res) => {
// Update a document // Update a document
app.put('/documents/:id', async (req, res) => { app.put('/documents/:id', async (req, res) => {
const { id } = req.params; const { id } = req.params;
const { title, body } = req.body; const { title, body, tags, edited_by } = req.body;
if (!title || !body) { if (!title || !body) {
return res.status(400).json({ error: 'Title and body are required' }); return res.status(400).json({ error: 'Title and body are required' });
} }
try { try {
const result = await db.collection('documents').updateOne( const result = await db.collection('documents').updateOne(
{ _id: new ObjectId(id) }, { _id: new ObjectId(id) },
{ $set: { title, body, updated_at: new Date() } } { $set: { title, body, tags, edited_by, updated_at: new Date() } }
); );
if (result.matchedCount === 0) { if (result.matchedCount === 0) {
return res.status(404).json({ error: 'Document not found' }); return res.status(404).json({ error: 'Document not found' });
@ -211,4 +216,5 @@ app.use((req, res) => {
// Start the server // Start the server
app.listen(port, () => { app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`); console.log(`Server running on http://localhost:${port}`);
console.log("Searching for database: URI: "+mongodb_uri);
}); });

View File

@ -332,7 +332,6 @@
"version": "16.4.7", "version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
"license": "BSD-2-Clause",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },

View File

@ -0,0 +1 @@
BACKEND_URI='http://localhost:3000'

View File

@ -25,6 +25,7 @@
"@tiptap/extension-underline": "^2.10.3", "@tiptap/extension-underline": "^2.10.3",
"@tiptap/pm": "^2.10.3", "@tiptap/pm": "^2.10.3",
"@tiptap/starter-kit": "^2.10.3", "@tiptap/starter-kit": "^2.10.3",
"dotenv": "^16.4.7",
"tippy.js": "^6.3.7" "tippy.js": "^6.3.7"
}, },
"devDependencies": { "devDependencies": {
@ -2046,6 +2047,17 @@
"url": "https://github.com/fb55/domhandler?sponsor=1" "url": "https://github.com/fb55/domhandler?sponsor=1"
} }
}, },
"node_modules/dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/eastasianwidth": { "node_modules/eastasianwidth": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",

View File

@ -51,6 +51,7 @@
"@tiptap/extension-underline": "^2.10.3", "@tiptap/extension-underline": "^2.10.3",
"@tiptap/pm": "^2.10.3", "@tiptap/pm": "^2.10.3",
"@tiptap/starter-kit": "^2.10.3", "@tiptap/starter-kit": "^2.10.3",
"dotenv": "^16.4.7",
"tippy.js": "^6.3.7" "tippy.js": "^6.3.7"
} }
} }

View File

@ -17,6 +17,7 @@
export function setHTML(html){ export function setHTML(html){
editor.commands.insertContent(html); editor.commands.insertContent(html);
//initialHTML = html;
} }
export function getHTML(){ export function getHTML(){

View File

@ -25,7 +25,7 @@
<i class="fa-solid fa-chevron-down ml-2 mr-2" style="color: #FFFFFF;"></i> <i class="fa-solid fa-chevron-down ml-2 mr-2" style="color: #FFFFFF;"></i>
{/if} {/if}
{:else} {:else}
<p class="font-bold text-white ml-1">Not Signed In</p> <i class="fa-solid fa-right-to-bracket" style="color: #FFFFFF;"></i>
<i class="fa-solid fa-chevron-down ml-2 mr-2" style="color: #FFFFFF;"></i> <i class="fa-solid fa-chevron-down ml-2 mr-2" style="color: #FFFFFF;"></i>
{/if} {/if}
</button> </button>

View File

@ -5,15 +5,15 @@
<script> <script>
import { page } from "$app/stores" import { page } from "$app/stores"
import Tiptap from '$lib/components/Tiptap.svelte' import Tiptap from '$lib/components/Tiptap.svelte'
import { PUBLIC_BASE_URL } from '$env/static/public';
let editorRef; //This is our reference to the tiptap editor let editorRef; //This is our reference to the tiptap editor
let document = {}; // To store fetched document data
let title = ""; let title = "";
async function save() { async function save() {
const url = "http://localhost:3000/documents"; const url = PUBLIC_BASE_URL+"/documents";
console.log("Title value:", title);
let created_by = $page.data.session.user?.name ?? "User"; let created_by = $page.data.session.user?.name ?? "User";
@ -31,11 +31,10 @@
title, title,
body, body,
tags, tags,
created_by created_by,
visibility
}; };
console.log(payload);
try { try {
const response = await fetch(url, { const response = await fetch(url, {
method: "POST", method: "POST",
@ -68,21 +67,22 @@
tags = tags.filter(t => t !== tag); // Filter out the tag from the array tags = tags.filter(t => t !== tag); // Filter out the tag from the array
} }
// Function to add a tag
function addTag() { function addTag() {
const tagInput = document.querySelector('#tag'); // Ensure the global `document` is used
const tagInput = window.document.querySelector('#tag');
const tag = tagInput.value.trim(); // Get input value and trim whitespace const tag = tagInput.value.trim(); // Get input value and trim whitespace
if (tag && !tags.includes(tag)) { // Ensure non-empty and no duplicates if (tag && !tags.includes(tag)) { // Ensure non-empty and no duplicates
tags = [...tags, tag]; // Create a new array to trigger reactivity tags = [...tags, tag]; // Create a new array to trigger reactivity
} }
tagInput.value = ''; // Clear the input field tagInput.value = ''; // Clear the input field
} }
let dropMenu = false; let dropMenu = false;
let visibility = 'Public'; let visibility = 'Public';
</script> </script>
<div class="flex items-center justify-center h-full flex-col mb-10"> <div class="flex items-center justify-center h-full flex-col mb-10">
<form class="flex-col justify-center w-11/12 md:w-10/12 xl:w-8/12 mt-20"> <div class="flex-col justify-center w-11/12 md:w-10/12 xl:w-8/12 mt-20">
<div class="flex flex-row w-full"> <div class="flex flex-row w-full">
<input <input
type="text" type="text"
@ -141,5 +141,5 @@
</button> </button>
</div> </div>
</div> </div>
</form> </div>
</div> </div>

View File

@ -1,6 +1,7 @@
<script lang="js"> <script lang="js">
import { page } from "$app/stores" import { page } from "$app/stores"
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { PUBLIC_BASE_URL } from '$env/static/public';
let document = null; // To store fetched document data let document = null; // To store fetched document data
let error = null; // To store any error that occurs during fetch let error = null; // To store any error that occurs during fetch
@ -18,7 +19,7 @@
try { try {
// Make the GET request to your API // Make the GET request to your API
const response = await fetch(`http://localhost:3000/documents/${id}`); const response = await fetch(PUBLIC_BASE_URL+`/documents/${id}`);
if (response.ok) { if (response.ok) {
document = await response.json(); // Store document data document = await response.json(); // Store document data
} else { } else {
@ -33,7 +34,7 @@
let agreeToDelete = false; let agreeToDelete = false;
// Function to delete a document by ID // Function to delete a document by ID
async function deleteDocument(id) { async function deleteDocument(id) {
const url = `http://localhost:3000/documents/${id}`; // Update with your API base URL if different const url = PUBLIC_BASE_URL+`/documents/${id}`; // Update with your API base URL if different
// Show the confirmation popup // Show the confirmation popup
alertPopup = true; alertPopup = true;
@ -87,9 +88,7 @@ async function deleteDocument(id) {
{#if alertPopup} {#if alertPopup}
<!-- Full-screen overlay to darken the background --> <!-- Full-screen overlay to darken the background -->
<div <div class="fixed inset-0 bg-gray-600 opacity-40 z-40"></div>
class="fixed inset-0 bg-gray-600 opacity-40 z-40"
></div>
<!-- Centered popup --> <!-- Centered popup -->
<div <div
@ -124,8 +123,8 @@ async function deleteDocument(id) {
<h1 class="text-2xl">{document.title}</h1> <h1 class="text-2xl">{document.title}</h1>
{#if $page.data.session} {#if $page.data.session}
<div> <div>
<a href="/edit?id={id}" class="bg-yellow-500 hover:bg-blue-400 border-double border-blue-100 border-2 p-3 rounded-full px-4 text-white font-roboto"><i class="fa-solid fa-pencil fa-lg"></i></a> <a href="/edit?id={id}"><button class="bg-yellow-500 hover:bg-blue-400 border-double border-blue-100 border-2 p-3 rounded-full px-4 text-white font-roboto"><i class="fa-solid fa-pencil fa-lg"></i></button></a>
<button class="bg-red-500 hover:bg-blue-400 border-double border-blue-100 border-2 p-3 rounded-full px-4 text-white font-roboto" on:click={() => { deleteDocument(documentId)}}><i class="fa-solid fa-trash fa-lg"></i></button> <button class="bg-red-500 hover:bg-blue-400 border-double border-blue-100 border-2 p-3 rounded-full px-4 text-white font-roboto" on:click={() => { deleteDocument(id)}}><i class="fa-solid fa-trash fa-lg"></i></button>
</div> </div>
{/if} {/if}
</div> </div>
@ -137,15 +136,21 @@ async function deleteDocument(id) {
</div> </div>
{/each} {/each}
</div> </div>
<div class="flex flex-row justify-between"> <div class="flex flex-row justify-between text-gray-400">
<div> <div>
{#if document.created_by} {#if document.created_by}
<p class="font-semibold">By: {document.created_by}</p> <p class="font-semibold">Created By: {document.created_by}</p>
{#if document.edited_by}
<p class="font-semibold">Edited By: {document.edited_by}</p>
{/if}
{/if} {/if}
</div> </div>
<div class=""> <div class="">
{#if document.created_at} {#if document.created_at}
<p class="font-semibold">{new Date(document.created_at).toLocaleString()}</p> <p class="font-semibold">Created: {new Date(document.created_at).toLocaleString()}</p>
{#if document.updated_at}
<p class="font-semibold">Updated: {new Date(document.updated_at).toLocaleString()}</p>
{/if}
{/if} {/if}
</div> </div>
</div> </div>

View File

@ -1,11 +1,12 @@
<svelte:head> <svelte:head>
<title>Add</title> <title>Edit</title>
</svelte:head> </svelte:head>
<script> <script>
import { onMount, onDestroy } from 'svelte'; import { onMount, onDestroy } from 'svelte';
import { page } from "$app/stores" import { page } from "$app/stores"
import Tiptap from '$lib/components/Tiptap.svelte' import Tiptap from '$lib/components/Tiptap.svelte'
import { PUBLIC_BASE_URL } from '$env/static/public';
let editorRef ; //This is our reference to the tiptap editor let editorRef ; //This is our reference to the tiptap editor
@ -15,14 +16,17 @@
let document = null; // To store fetched document data let document = null; // To store fetched document data
let error = null; // To store any error that occurs during fetch let error = null; // To store any error that occurs during fetch
let documentId; let documentId;
let tags = [];
// Function to fetch the document by ID // Function to fetch the document by ID
async function fetchDocumentById(id) { async function fetchDocumentById(id) {
try { try {
const response = await fetch(`http://localhost:3000/documents/${id}`); const response = await fetch(PUBLIC_BASE_URL+`/documents/${id}`);
if (response.ok) { if (response.ok) {
const fetchedDocument = await response.json(); const fetchedDocument = await response.json();
document = fetchedDocument; // Update document data document = fetchedDocument; // Update document data
title = document.title;
tags = document.tags;
console.log(document.body); console.log(document.body);
editorRef.setHTML(document.body); // Update the editor with the HTML editorRef.setHTML(document.body); // Update the editor with the HTML
} else { } else {
@ -53,11 +57,11 @@
async function save() { async function save() {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const id = urlParams.get('id'); // Get the `id` from the query string const id = urlParams.get('id'); // Get the `id` from the query string
const url = "http://localhost:3000/documents/"+id; const url = PUBLIC_BASE_URL+"/documents/"+id;
console.log("Title value:", title); console.log("Title value:", title);
let created_by = $page.data.session.user?.name ?? "User"; let edited_by = $page.data.session.user?.name ?? "User";
// Get the HTML from the tiptap editor // Get the HTML from the tiptap editor
if (editorRef) { if (editorRef) {
@ -75,11 +79,9 @@
title, title,
body, body,
tags, tags,
created_by edited_by
}; };
console.log(payload);
try { try {
const response = await fetch(url, { const response = await fetch(url, {
method: "PUT", method: "PUT",
@ -105,8 +107,6 @@
} }
let tags = [];
// Function to remove a tag // Function to remove a tag
function removeTag(tag) { function removeTag(tag) {
tags = tags.filter(t => t !== tag); // Filter out the tag from the array tags = tags.filter(t => t !== tag); // Filter out the tag from the array
@ -114,7 +114,7 @@
// Function to add a tag // Function to add a tag
function addTag() { function addTag() {
const tagInput = document.querySelector('#tag'); const tagInput = window.document.querySelector('#tag');
const tag = tagInput.value.trim(); // Get input value and trim whitespace const tag = tagInput.value.trim(); // Get input value and trim whitespace
if (tag && !tags.includes(tag)) { // Ensure non-empty and no duplicates if (tag && !tags.includes(tag)) { // Ensure non-empty and no duplicates
tags = [...tags, tag]; // Create a new array to trigger reactivity tags = [...tags, tag]; // Create a new array to trigger reactivity
@ -126,7 +126,7 @@
</script> </script>
<div class="flex items-center justify-center h-full flex-col mb-10"> <div class="flex items-center justify-center h-full flex-col mb-10">
<form class="flex-col justify-center w-11/12 md:w-10/12 xl:w-8/12 mt-20"> <div class="flex-col justify-center w-11/12 md:w-10/12 xl:w-8/12 mt-20">
<div class="flex flex-row w-full"> <div class="flex flex-row w-full">
<input <input
type="text" type="text"
@ -185,5 +185,5 @@
</button> </button>
</div> </div>
</div> </div>
</form> </div>
</div> </div>

View File

@ -1,6 +1,7 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { page } from "$app/stores" import { page } from "$app/stores"
import { PUBLIC_BASE_URL } from '$env/static/public';
let query = false; let query = false;
let searchQuery = ""; let searchQuery = "";
@ -23,7 +24,7 @@
// Function to simulate search API call // Function to simulate search API call
async function searchDocuments(query) { async function searchDocuments(query) {
try { try {
const res = await fetch(`http://localhost:3000/search?query=${query}`); const res = await fetch(PUBLIC_BASE_URL+`/search?query=${query}`);
const data = await res.json(); const data = await res.json();
if (data.length > 0) { if (data.length > 0) {
@ -40,7 +41,7 @@
// Function to fetch all documents from the API // Function to fetch all documents from the API
async function getAllDocuments() { async function getAllDocuments() {
try { try {
const res = await fetch(`http://localhost:3000/documents`); const res = await fetch(PUBLIC_BASE_URL+`/documents`);
if (!res.ok) { if (!res.ok) {
throw new Error(`Failed to fetch documents: ${res.statusText}`); throw new Error(`Failed to fetch documents: ${res.statusText}`);