[dyad] wrote 6 file(s)

This commit is contained in:
Michael committed 2025-11-06 14:03:16 +01:00
1 parent f45cfb36e0
commit dd58d35af7
6 files changed
+116 -35

No files matched your search

+13 -1
View File
@@ -1,5 +1,17 @@
const jwt = require('jsonwebtoken');
const { logger } = require('../config/database');
const winston = require('winston');
// Configure logger
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console()
]
});
const authMiddleware = (req, res, next) => {
try {
+1 -2
View File
@@ -5,8 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Notes & Todos</title>
<link rel="stylesheet" href="/css/styles.css">
<!-- Remplacer les CDN Lucide par une version locale -->
<script src="/js/lucide.min.js" defer></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body class="bg-[#FAFAFA] text-gray-900">
<!-- Auth Container -->
+7 -2
View File
@@ -10,7 +10,7 @@ class App {
// Load initial data if authenticated
if (this.auth.token) {
this.notes.loadNotes();
if (this.auth.user.is_admin) {
if (this.auth.user && this.auth.user.is_admin) {
this.admin.loadUsers();
}
}
@@ -18,4 +18,9 @@ class App {
}
// Initialize main app
window.app = new App();
window.app = new App();
// Initialize app when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
window.app.init();
});
+15 -8
View File
@@ -1,7 +1,7 @@
class AuthService {
constructor() {
this.token = localStorage.getItem('token');
this.user = JSON.parse(localStorage.getItem('user'));
this.user = JSON.parse(localStorage.getItem('user') || 'null');
this.loginForm = document.getElementById('login-form');
this.logoutBtn = document.getElementById('logout-btn');
this.userInfo = document.getElementById('user-info');
@@ -11,8 +11,12 @@ class AuthService {
}
setupEventListeners() {
this.loginForm.addEventListener('submit', (e) => this.handleLogin(e));
this.logoutBtn.addEventListener('click', () => this.handleLogout());
if (this.loginForm) {
this.loginForm.addEventListener('submit', (e) => this.handleLogin(e));
}
if (this.logoutBtn) {
this.logoutBtn.addEventListener('click', () => this.handleLogout());
}
}
async handleLogin(e) {
@@ -66,11 +70,14 @@ class AuthService {
// Show admin panel button if user is admin
if (this.user.is_admin) {
const adminBtn = document.createElement('button');
adminBtn.className = 'ml-4 text-sm text-gray-600 hover:text-gray-900';
adminBtn.textContent = 'Admin';
adminBtn.onclick = () => adminPanel.classList.remove('hidden');
this.userInfo.parentNode.insertBefore(adminBtn, this.logoutBtn);
const adminBtnExists = document.querySelector('.admin-btn');
if (!adminBtnExists) {
const adminBtn = document.createElement('button');
adminBtn.className = 'admin-btn ml-4 text-sm text-gray-600 hover:text-gray-900';
adminBtn.textContent = 'Admin';
adminBtn.onclick = () => adminPanel.classList.remove('hidden');
this.userInfo.parentNode.insertBefore(adminBtn, this.logoutBtn);
}
}
} else {
authContainer.classList.remove('hidden');
+63 -20
View File
@@ -18,27 +18,52 @@ class NotesService {
this.imageUpload = document.getElementById('image-upload');
this.setupEventListeners();
this.loadNotes();
}
setupEventListeners() {
this.newNoteBtn.addEventListener('click', () => this.createNewNote());
this.archiveNoteBtn.addEventListener('click', () => this.toggleArchiveNote());
this.deleteNoteBtn.addEventListener('click', () => this.deleteNote());
this.addTodoBtn.addEventListener('click', () => this.addTodo());
this.imageUpload.addEventListener('change', (e) => this.handleImageUpload(e));
this.searchInput.addEventListener('input', (e) => this.handleSearch(e));
if (this.newNoteBtn) {
this.newNoteBtn.addEventListener('click', () => this.createNewNote());
}
if (this.archiveNoteBtn) {
this.archiveNoteBtn.addEventListener('click', () => this.toggleArchiveNote());
}
if (this.deleteNoteBtn) {
this.deleteNoteBtn.addEventListener('click', () => this.deleteNote());
}
if (this.addTodoBtn) {
this.addTodoBtn.addEventListener('click', () => this.addTodo());
}
if (this.imageUpload) {
this.imageUpload.addEventListener('change', (e) => this.handleImageUpload(e));
}
if (this.searchInput) {
this.searchInput.addEventListener('input', (e) => this.handleSearch(e));
}
// Auto-save on content changes
this.noteTitle.addEventListener('input', () => this.autoSave());
this.noteContent.addEventListener('input', () => this.autoSave());
if (this.noteTitle) {
this.noteTitle.addEventListener('input', () => this.autoSave());
}
if (this.noteContent) {
this.noteContent.addEventListener('input', () => this.autoSave());
}
}
async loadNotes() {
if (!window.auth.token) {
console.log('Not authenticated, skipping notes loading');
return;
}
try {
const response = await fetch('/api/notes', {
headers: window.auth.getHeaders()
});
if (!response.ok) {
throw new Error(`Failed to load notes: ${response.status}`);
}
this.notes = await response.json();
this.renderNotesList();
} catch (error) {
@@ -47,12 +72,19 @@ class NotesService {
}
renderNotesList(searchTerm = '') {
if (!this.notesList) return;
this.notesList.innerHTML = '';
if (!this.notes || !this.notes.length) {
this.notesList.innerHTML = '<div class="p-2 text-gray-500">Aucune note</div>';
return;
}
const filteredNotes = searchTerm
? this.notes.filter(note =>
note.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
note.content.toLowerCase().includes(searchTerm.toLowerCase())
(note.content && note.content.toLowerCase().includes(searchTerm.toLowerCase()))
)
: this.notes;
@@ -70,14 +102,16 @@ class NotesService {
selectNote(note) {
this.currentNote = note;
this.noteTitle.value = note.title;
this.noteContent.innerHTML = note.content;
if (this.noteTitle) this.noteTitle.value = note.title || '';
if (this.noteContent) this.noteContent.innerHTML = note.content || '';
this.renderTodos();
this.renderImages();
this.renderNotesList(); // Update active state
}
renderTodos() {
if (!this.todosList || !this.currentNote || !this.currentNote.todos) return;
this.todosList.innerHTML = '';
this.currentNote.todos.forEach((todo, index) => {
const todoElement = document.createElement('div');
@@ -101,8 +135,10 @@ class NotesService {
}
renderImages() {
if (!this.imagesList || !this.currentNote || !this.currentNote.images) return;
this.imagesList.innerHTML = '';
this.currentNote.images.forEach((image, index) => {
this.currentNote.images.forEach((image) => {
const imageElement = document.createElement('div');
imageElement.className = 'image-thumbnail';
imageElement.innerHTML = `
@@ -199,10 +235,10 @@ class NotesService {
this.notes.splice(index, 1);
this.currentNote = null;
this.renderNotesList();
this.noteTitle.value = '';
this.noteContent.innerHTML = '';
this.todosList.innerHTML = '';
this.imagesList.innerHTML = '';
if (this.noteTitle) this.noteTitle.value = '';
if (this.noteContent) this.noteContent.innerHTML = '';
if (this.todosList) this.todosList.innerHTML = '';
if (this.imagesList) this.imagesList.innerHTML = '';
} catch (error) {
console.error('Error deleting note:', error);
}
@@ -214,13 +250,17 @@ class NotesService {
const text = prompt('Nouveau todo:');
if (!text) return;
if (!this.currentNote.todos) {
this.currentNote.todos = [];
}
this.currentNote.todos.push({ text, completed: false });
this.renderTodos();
this.autoSave();
}
async toggleTodo(index) {
if (!this.currentNote) return;
if (!this.currentNote || !this.currentNote.todos) return;
this.currentNote.todos[index].completed = !this.currentNote.todos[index].completed;
this.renderTodos();
@@ -228,7 +268,7 @@ class NotesService {
}
async deleteTodo(index) {
if (!this.currentNote) return;
if (!this.currentNote || !this.currentNote.todos) return;
this.currentNote.todos.splice(index, 1);
this.renderTodos();
@@ -249,6 +289,9 @@ class NotesService {
});
const image = await response.json();
if (!this.currentNote.images) {
this.currentNote.images = [];
}
this.currentNote.images.push(image);
this.renderImages();
} catch (error) {
@@ -259,7 +302,7 @@ class NotesService {
}
async deleteImage(imageId) {
if (!this.currentNote || !confirm('Supprimer cette image ?')) return;
if (!this.currentNote || !this.currentNote.images || !confirm('Supprimer cette image ?')) return;
try {
await fetch(`/api/notes/${this.currentNote.id}/images/${imageId}`, {
+17 -2
View File
@@ -3,8 +3,15 @@ const cors = require('cors');
const helmet = require('helmet');
const path = require('path');
const winston = require('winston');
const fs = require('fs');
const { db } = require('./config/database');
// Ensure uploads directory exists
const uploadsDir = path.join(__dirname, 'public/uploads');
if (!fs.existsSync(uploadsDir)) {
fs.mkdirSync(uploadsDir, { recursive: true });
}
// Configure logger
const logger = winston.createLogger({
level: 'info',
@@ -25,9 +32,11 @@ app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
imgSrc: ["'self'", "data:"],
connectSrc: ["'self'"]
}
}
}));
@@ -35,6 +44,12 @@ app.use(cors());
app.use(express.json());
app.use(express.static('public'));
// Set JWT_SECRET environment variable if not set
if (!process.env.JWT_SECRET) {
process.env.JWT_SECRET = 'default_development_secret';
logger.warn('JWT_SECRET not set, using default (insecure) value');
}
// Routes
app.use('/api/auth', require('./routes/auth.routes'));
app.use('/api/users', require('./routes/users.routes'));