AI Coach messages bookmarking. Top bar refined.
This commit is contained in:
22
server/.env
Normal file
22
server/.env
Normal file
@@ -0,0 +1,22 @@
|
||||
# Generic
|
||||
|
||||
# DEV
|
||||
DATABASE_URL_DEV="file:./prisma/dev.db"
|
||||
ADMIN_EMAIL_DEV="admin@gymflow.ai"
|
||||
ADMIN_PASSWORD_DEV="admin123"
|
||||
|
||||
# TEST
|
||||
DATABASE_URL_TEST="file:./prisma/test.db"
|
||||
ADMIN_EMAIL_TEST="admin@gymflow.ai"
|
||||
ADMIN_PASSWORD_TEST="admin123"
|
||||
|
||||
# PROD
|
||||
DATABASE_URL_PROD="file:./prisma/prod.db"
|
||||
ADMIN_EMAIL_PROD="admin-prod@gymflow.ai"
|
||||
ADMIN_PASSWORD_PROD="secure-prod-password-change-me"
|
||||
|
||||
# Fallback for Prisma CLI (Migrate default)
|
||||
DATABASE_URL="file:./prisma/dev.db"
|
||||
|
||||
GEMINI_API_KEY=AIzaSyC88SeFyFYjvSfTqgvEyr7iqLSvEhuadoE
|
||||
DEFAULT_EXERCISES_CSV_PATH='default_exercises.csv'
|
||||
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "SavedMessage" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"userId" TEXT NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
"role" TEXT NOT NULL DEFAULT 'model',
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "SavedMessage_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
@@ -24,6 +24,16 @@ model User {
|
||||
exercises Exercise[]
|
||||
plans WorkoutPlan[]
|
||||
weightRecords BodyWeightRecord[]
|
||||
savedMessages SavedMessage[]
|
||||
}
|
||||
|
||||
model SavedMessage {
|
||||
id String @id @default(uuid())
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
content String
|
||||
role String @default("model")
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model BodyWeightRecord {
|
||||
|
||||
BIN
server/prod.db
BIN
server/prod.db
Binary file not shown.
79
server/src/controllers/bookmarks.controller.ts
Normal file
79
server/src/controllers/bookmarks.controller.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Request, Response } from 'express';
|
||||
import prisma from '../lib/prisma';
|
||||
|
||||
export class BookmarksController {
|
||||
static async getAll(req: Request, res: Response) {
|
||||
try {
|
||||
const userId = (req as any).user?.userId;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ success: false, error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
const messages = await prisma.savedMessage.findMany({
|
||||
where: { userId },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
|
||||
return res.json({ success: true, data: messages });
|
||||
} catch (error) {
|
||||
console.error('Failed to get bookmarks:', error);
|
||||
return res.status(500).json({ success: false, error: 'Internal server error' });
|
||||
}
|
||||
}
|
||||
|
||||
static async create(req: Request, res: Response) {
|
||||
try {
|
||||
const userId = (req as any).user?.userId;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ success: false, error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
const { content, role } = req.body;
|
||||
if (!content) {
|
||||
return res.status(400).json({ success: false, error: 'Content is required' });
|
||||
}
|
||||
|
||||
const message = await prisma.savedMessage.create({
|
||||
data: {
|
||||
userId,
|
||||
content,
|
||||
role: role || 'model',
|
||||
},
|
||||
});
|
||||
|
||||
return res.json({ success: true, data: message });
|
||||
} catch (error) {
|
||||
console.error('Failed to create bookmark:', error);
|
||||
return res.status(500).json({ success: false, error: 'Internal server error' });
|
||||
}
|
||||
}
|
||||
|
||||
static async delete(req: Request, res: Response) {
|
||||
try {
|
||||
const userId = (req as any).user?.userId;
|
||||
if (!userId) {
|
||||
return res.status(401).json({ success: false, error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
// Verify ownership
|
||||
const existing = await prisma.savedMessage.findFirst({
|
||||
where: { id, userId },
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
return res.status(404).json({ success: false, error: 'Bookmark not found' });
|
||||
}
|
||||
|
||||
await prisma.savedMessage.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Failed to delete bookmark:', error);
|
||||
return res.status(500).json({ success: false, error: 'Internal server error' });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import sessionRoutes from './routes/sessions';
|
||||
import planRoutes from './routes/plans';
|
||||
import aiRoutes from './routes/ai';
|
||||
import weightRoutes from './routes/weight';
|
||||
import bookmarksRoutes from './routes/bookmarks';
|
||||
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
@@ -88,6 +89,7 @@ app.use('/api/sessions', sessionRoutes);
|
||||
app.use('/api/plans', planRoutes);
|
||||
app.use('/api/ai', aiRoutes);
|
||||
app.use('/api/weight', weightRoutes);
|
||||
app.use('/api/bookmarks', bookmarksRoutes);
|
||||
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
|
||||
13
server/src/routes/bookmarks.ts
Normal file
13
server/src/routes/bookmarks.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import express from 'express';
|
||||
import { BookmarksController } from '../controllers/bookmarks.controller';
|
||||
import { authenticateToken } from '../middleware/auth';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.use(authenticateToken);
|
||||
|
||||
router.get('/', BookmarksController.getAll);
|
||||
router.post('/', BookmarksController.create);
|
||||
router.delete('/:id', BookmarksController.delete);
|
||||
|
||||
export default router;
|
||||
BIN
server/test.db
BIN
server/test.db
Binary file not shown.
Reference in New Issue
Block a user