diff --git a/.gitignore b/.gitignore index 24bbf88..2162829 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ node_modules PROMPTS.md To-do.md Claude/ -.claude/ \ No newline at end of file +.claude/ diff --git a/api-examples/signup.rest b/api-examples/signup.rest index aa53c87..2a318c4 100644 --- a/api-examples/signup.rest +++ b/api-examples/signup.rest @@ -1,8 +1,8 @@ -POST http://localhost:3000/auth/signup +POST https://the-locals.deno.dev/auth/signup Content-Type: application/json { - "email": "Dkjdhsbfjewrbk@gmail.com", - "password": "securePassword123", - "username": "Donald-kjhsdbvcck" + "email": "Dkjdhsbfje@gmail.com", + "password": "securePasswod1", + "username": "Donald-kjhsdb" } diff --git a/deno.json b/deno.json index cfe60d1..2582fc7 100644 --- a/deno.json +++ b/deno.json @@ -8,7 +8,6 @@ "event-test": "deno run --allow-read --allow-write testing/testScripts/event.utils.test.ts" }, "imports": { - "models/": "./shared/src/models/", "mongodb": "npm:mongodb", "zod": "npm:zod", "@openai/openai": "jsr:@openai/openai@^4.98.0" diff --git a/deno.lock b/deno.lock index 5ca1e50..73355fa 100644 --- a/deno.lock +++ b/deno.lock @@ -163,6 +163,12 @@ "https://deno.land/std@0.224.0/dotenv/mod.ts": "0180eaeedaaf88647318811cdaa418cc64dc51fb08354f91f5f480d0a1309f7d", "https://deno.land/std@0.224.0/dotenv/parse.ts": "09977ff88dfd1f24f9973a338f0f91bbdb9307eb5ff6085446e7c423e4c7ba0c", "https://deno.land/std@0.224.0/dotenv/stringify.ts": "275da322c409170160440836342eaa7cf012a1d11a7e700d8ca4e7f2f8aa4615", + "https://deno.land/std@0.61.0/encoding/utf8.ts": "8654fa820aa69a37ec5eb11908e20b39d056c9bf1c23ab294303ff467f3d50a1", + "https://deno.land/x/bcrypt@v0.2.4/deps.ts": "dc2253e8aed3c5670f096fe9515913f644b650bae7212d8f6af5c14b88350461", + "https://deno.land/x/bcrypt@v0.2.4/mod.ts": "8eaac7e28fd305228e5994de60032b743282bfeadd5060c1759288d27f32d28e", + "https://deno.land/x/bcrypt@v0.2.4/src/bcrypt/base64.ts": "b8266450a4f1eb6960f60f2f7986afc4dde6b45bd2d7ee7ba10789e67e17b9f7", + "https://deno.land/x/bcrypt@v0.2.4/src/bcrypt/bcrypt.ts": "7845b01323458b530dcf6e9ab46e513c507b6ff1670457afaa0b3465721b56b4", + "https://deno.land/x/bcrypt@v0.2.4/src/main.ts": "4ac3ccc2247ce729f6c7d57654909949b62aafdabd996483c97f34aa1afac82a", "https://deno.land/x/bcrypt@v0.4.1/mod.ts": "ff09bdae282583cf5f7d87efe37ddcecef7f14f6d12e8b8066a3058db8c6c2f7", "https://deno.land/x/bcrypt@v0.4.1/src/bcrypt/base64.ts": "b8266450a4f1eb6960f60f2f7986afc4dde6b45bd2d7ee7ba10789e67e17b9f7", "https://deno.land/x/bcrypt@v0.4.1/src/bcrypt/bcrypt.ts": "ec221648cc6453ea5e3803bc817c01157dada06aa6f7a0ba6b9f87aae32b21e2", @@ -224,7 +230,10 @@ "https://deno.land/x/zod@v3.24.4/locales/en.ts": "a7a25cd23563ccb5e0eed214d9b31846305ddbcdb9c5c8f508b108943366ab4c", "https://deno.land/x/zod@v3.24.4/mod.ts": "ec6e2b1255c1a350b80188f97bd0a6bac45801bb46fc48f50b9763aa66046039", "https://deno.land/x/zod@v3.24.4/standard-schema.ts": "4abb2e7bd784fb95d219584673971bb317e74fb4fd0c74c196b558ba46df4456", - "https://deno.land/x/zod@v3.24.4/types.ts": "cfdd77407d4b06e79bd63e9cfdb6091489aeb5f432d03a68c0531c1965d001fb" + "https://deno.land/x/zod@v3.24.4/types.ts": "cfdd77407d4b06e79bd63e9cfdb6091489aeb5f432d03a68c0531c1965d001fb", + "https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/deps.ts": "fdb5c487e374fb289d37836f0c89ce1f1d0a56c131e01c10cb797aec1e5a8f7f", + "https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/event.model.ts": "ef793ba4fe8a964fbddbeb9b38440810db0ccbd257afbde9592ba336ec3eafc7", + "https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/user.model.ts": "1e863d6b3803d775c5784e3dfd9d8374c379f46c5d66e64c5c442bd10effacd4" }, "workspace": { "dependencies": [ diff --git a/deps.ts b/deps.ts index 5cd6ff6..bfb4442 100644 --- a/deps.ts +++ b/deps.ts @@ -29,4 +29,8 @@ export type { Payload } from 'https://deno.land/x/djwt@v3.0.2/mod.ts'; export { Db, MongoClient, ObjectId } from 'npm:mongodb@6.1.0'; export type { OptionalId } from 'npm:mongodb@6.1.0'; -export { compare, hash } from 'https://deno.land/x/bcrypt@v0.4.1/mod.ts'; +//more stable version of bycrypt +export { + compareSync, + hashSync, +} from 'https://deno.land/x/bcrypt@v0.2.4/mod.ts'; diff --git a/shared b/shared deleted file mode 160000 index bddcaa7..0000000 --- a/shared +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bddcaa7804d4987f6cb84dc19d07359c1c16a8c7 diff --git a/src/app.ts b/src/app.ts index 9b802f0..6bf1d7c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,13 +1,18 @@ import 'https://deno.land/std@0.224.0/dotenv/load.ts'; import { Application, oakCors } from '../deps.ts'; import './database/connect.ts'; +import './cron.ts'; import router from './routes/index.ts'; import logRequest from './middleware/logRequest.ts'; const app = new Application(); app.use( - oakCors({ origin: ['http://localhost:5173', 'http://127.0.0.1:5173'] }), + oakCors({ + origin: ['https://the-locals.netlify.app', 'http://localhost:5173'], + credentials: true, + optionsSuccessStatus: 200, // For legacy browser support + }), ); // Allow local frontend to bypass cors requirement app.use(logRequest); app.use(router.routes()); diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts index 9499db8..92ed325 100644 --- a/src/controllers/auth.controller.ts +++ b/src/controllers/auth.controller.ts @@ -1,6 +1,9 @@ // deno-lint-ignore-file require-await import { Context, RouterContext, Status } from '../../deps.ts'; -import { UserLogInSchema, UserSignUpSchema } from 'models/user.model.ts'; +import { + UserLogInSchema, + UserSignUpSchema, +} from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/user.model.ts'; import { authService } from '../services/auth.service.ts'; export const getCurrentUser = async (ctx: Context) => { @@ -19,9 +22,13 @@ export const getCurrentUser = async (ctx: Context) => { export const signUpUser = async (ctx: RouterContext<'/signup'>) => { const body = await ctx.request.body.json(); + console.log('Request body:', body); + const userInput = UserSignUpSchema.safeParse(body); + console.log('Validtion result:', userInput.success); if (!userInput.success) { + console.log('Validation Errors:', userInput.error); ctx.response.status = Status.BadRequest; ctx.response.body = { errors: userInput.error.flatten() }; return; @@ -33,6 +40,7 @@ export const signUpUser = async (ctx: RouterContext<'/signup'>) => { } catch (error) { ctx.response.status = Status.InternalServerError; if (error instanceof Error) { + console.log('Sign up user error:', error); ctx.response.body = { error: error.message }; } else { ctx.response.body = { error: 'Unkown error creating user' }; diff --git a/src/controllers/event.controller.ts b/src/controllers/event.controller.ts index faebce5..f4c799d 100644 --- a/src/controllers/event.controller.ts +++ b/src/controllers/event.controller.ts @@ -1,27 +1,40 @@ import { Context, ObjectId, RouterContext, Status } from '../../deps.ts'; import { eventService } from '../services/event.service.ts'; -import { generateEvents } from '../services/openai.service.ts'; -import { eventFilterSchema, FullEvent } from 'models/event.model.ts'; +//import { generateEvents } from '../services/openai.service.ts'; +import { + eventFilterSchema, + FullEvent, +} from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/event.model.ts'; +import { verifyToken } from '../utils/token.utils.ts'; +import { Payload } from '../../deps.ts'; +import { userService } from '../services/user.service.ts'; export const getAllEvents = async (ctx: Context) => { - const params = ctx.request.url.searchParams; + const auth = ctx.request.headers.get('Authorization'); + const token = auth && auth.split(' ')[1]; + + let userId: string | null = null; + if (token) { + try { + const user: Payload = await verifyToken(token); + userId = user._id as string; + } catch (error) { + console.warn('Invalid token in /events: ' + error); + } + } + const params = ctx.request.url.searchParams; const rawModes = params.getAll('mode'); - const normalizedModes = rawModes .flatMap((param) => param.split(',')) .map((mode) => mode.trim().toLowerCase()) - .filter(Boolean); // removes empty strings + .filter(Boolean); const parseResult = eventFilterSchema.safeParse({ mode: normalizedModes.length > 0 ? normalizedModes : undefined, }); - let allEvents; - if (parseResult.success) { - console.log(parseResult.data); - allEvents = await eventService.getAllEvents(parseResult.data); - } else { + if (!parseResult.success) { ctx.response.status = Status.BadRequest; ctx.response.body = { error: 'Invalid query parameters', @@ -30,7 +43,21 @@ export const getAllEvents = async (ctx: Context) => { return; } - ctx.response.body = allEvents; + try { + const [allEvents, savedEvents] = await Promise.all([ + eventService.getAllEvents(parseResult.data), + userId ? userService.getUserEvents(userId) : Promise.resolve([]), + ]); + + ctx.response.body = { + events: allEvents, + savedEventIds: savedEvents.map((x) => x._id), + }; + } catch (err) { + console.error('Error fetching combined event data:', err); + ctx.response.status = Status.InternalServerError; + ctx.response.body = { error: 'Internal server error' }; + } }; export const getEventById = async (ctx: RouterContext<'/:id'>) => { @@ -64,8 +91,8 @@ export const updateEventById = async (ctx: RouterContext<'/:id'>) => { } const id: string = ctx.params.id; - const event: FullEvent = await ctx.request.body.json(); - + const event: Partial = await ctx.request.body.json(); + console.log(event); if (!ObjectId.isValid(id)) { ctx.response.status = Status.BadRequest; ctx.response.body = `Invalid event id "${id}"`; @@ -129,33 +156,43 @@ export const saveNewEvent = async (ctx: Context) => { } }; -export const saveEventsCronHandler = async (ctx: Context) => { - console.log('Server pinged'); - const token = ctx.request.headers.get('X-Daily-Token'); - - // Change this to appropriate .env or github secret - const expectedToken = Deno.env.get('DAILY_JOB_TOKEN'); - - if (token !== expectedToken) { - ctx.response.status = Status.Forbidden; - ctx.response.body = 'Forbidden'; - return; - } - - console.log('Daily task triggered'); - - const events = await generateEvents( - ['music', 'charity', 'sports', 'other'], - 'Finsbury Park', - ); - - if (events !== null) { - await eventService.saveEvents(events); - ctx.response.body = 'Events saved sucessfully'; - console.log('Events saved'); - } else { - ctx.response.status = Status.NoContent; - ctx.response.body = 'No events to save'; - console.log('There were no events to save'); - } -}; +// export const saveEventsCronHandler = async (ctx: Context) => { +// console.log('Called saveEventsCronHandler()'); +// const token = ctx.request.headers.get('X-Daily-Token'); + +// // Change this to appropriate .env or github secret +// const expectedToken = Deno.env.get('DAILY_JOB_TOKEN'); + +// if (token !== expectedToken) { +// ctx.response.status = Status.Forbidden; +// ctx.response.body = 'Forbidden'; +// return; +// } + +// console.log('Daily task triggered'); + +// const events = await generateEvents( +// ['music', 'charity', 'sports', 'other'], +// 'Finsbury Park', +// ); + +// if (events !== null) { +// await eventService.saveEvents(events); +// ctx.response.body = 'Events saved sucessfully'; +// console.log('Events saved'); +// } else { +// ctx.response.status = Status.NoContent; +// ctx.response.body = 'No events to save'; +// console.log('There were no events to save'); +// } +// }; + +// export const updateEvent = async (ctx: Context) => { +// // TODO: Update event in DB +// ctx.response.body = { message: `Update event of params.id` }; +// }; + +// export const deleteEvent = async (ctx: Context) => { +// // TODO: Delete event from DB +// ctx.response.body = { message: `Delete event params.id` }; +// }; diff --git a/src/controllers/user.controller.ts b/src/controllers/user.controller.ts index d6f52cd..242874d 100644 --- a/src/controllers/user.controller.ts +++ b/src/controllers/user.controller.ts @@ -1,8 +1,8 @@ // deno-lint-ignore-file require-await -import { Context, RouterContext } from '../../deps.ts'; +import { Context, Payload, RouterContext } from '../../deps.ts'; import { userService } from '../services/user.service.ts'; import { Status } from '../../deps.ts'; -import { toSafeUser } from 'models/user.model.ts'; +import { toSafeUser } from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/user.model.ts'; export const getUserProfile = async (ctx: Context) => { // TODO: Get user data from ctx.state.user ctx.response.body = { message: 'Get user profile' }; @@ -17,7 +17,7 @@ export const deleteUserAccount = async (ctx: Context) => { // TODO: Call userService.deleteUser(ctx.state.user.id) ctx.response.body = { message: 'Delete user account' }; }; -export const getAllUsers = async (ctx: RouterContext<'/:role'>) => { +export const getAllUsers = async (ctx: RouterContext<'/getUsers:role'>) => { const role = ctx.params.role; if (role !== 'user' && role !== 'admin' && role !== 'all') { @@ -40,3 +40,23 @@ export const getAllUsers = async (ctx: RouterContext<'/:role'>) => { } } }; + +export const handleUserEvents = async (ctx: Context) => { + try { + const user: Payload = ctx.state.user; + + if (!user || !user._id) { + ctx.response.status = Status.Unauthorized; + ctx.response.body = { message: 'Unauthorized: Invalid token' }; + return; + } + const body = await ctx.request.body.json(); + const { eventId, active } = body; + const userId = user._id as string; + + await userService.handleUserEvents(eventId, userId, active); + return ctx.response.body = 'User event handled'; + } catch (error) { + console.error('Issue saving user events: ' + error); + } +}; diff --git a/src/cron.ts b/src/cron.ts new file mode 100644 index 0000000..82e875e --- /dev/null +++ b/src/cron.ts @@ -0,0 +1,29 @@ +import { eventService } from './services/event.service.ts'; +import { generateEvents } from './services/openai.service.ts'; + +Deno.cron('save events cron', '0 0 * * *', async () => { + console.log('executing cron job...'); + try { + console.log('Daily task triggered'); + + const events = await generateEvents( + ['music', 'charity', 'sports', 'other'], + 'Finsbury Park', + ); + + if (events !== null) { + await eventService.saveEvents(events); + console.log('Events saved successfully'); + } else { + console.log('There were no events to save'); + } + } catch (error) { + console.error('Cron job failed:', error); + } +}); + +//cron for every 10mins +// "*/10 * * * *" + +// cron for midnight +// "0 0 * * *" diff --git a/src/routes/event.routes.ts b/src/routes/event.routes.ts index b78f17b..dc410fe 100644 --- a/src/routes/event.routes.ts +++ b/src/routes/event.routes.ts @@ -3,7 +3,7 @@ import { deleteEventById, getAllEvents, getEventById, - saveEventsCronHandler, + //saveEventsCronHandler, saveNewEvent, updateEventById, } from '../controllers/event.controller.ts'; @@ -20,7 +20,7 @@ router.get('/:id', getEventById); router.put('/:id', ProtectRoute, protectAdmin, updateEventById); router.delete('/:id', ProtectRoute, protectAdmin, deleteEventById); router.post('/save-event', saveNewEvent); -router.post('/cron/save-events', saveEventsCronHandler); +//router.post('/cron/save-events', saveEventsCronHandler); // router.post("/") // router.post("/generate", generateEvents) (using the openAi service) diff --git a/src/routes/user.routes.ts b/src/routes/user.routes.ts index 36ff637..fabb53b 100644 --- a/src/routes/user.routes.ts +++ b/src/routes/user.routes.ts @@ -1,19 +1,13 @@ import { Router } from '../../deps.ts'; import protectAdmin from '../middleware/requireAdmin.ts'; import ProtectRoute from '../middleware/protectRoute.ts'; -import { getAllUsers } from '../controllers/user.controller.ts'; -const router = new Router(); - -// Routes under /users +import { + getAllUsers, + handleUserEvents, +} from '../controllers/user.controller.ts'; -// Temporary stub for testing -router.get('/', (ctx) => { - ctx.response.body = 'User route root'; -}); -router.get('/:role', ProtectRoute, protectAdmin, getAllUsers); -// -> to controllers -// router.get("/me", getUserProfile) -// router.put("/me", updateUserProfile) -// router.delete("/me", deleteUserAccount) +const router = new Router(); +router.get('/getUsers:role', ProtectRoute, protectAdmin, getAllUsers); +router.post('/saveUserEvents', ProtectRoute, handleUserEvents); export default router; diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 1a1e2e9..3b505dc 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -5,48 +5,67 @@ import { UserInDB, UserLogInInput, UserSignUpInput, -} from 'models/user.model.ts'; -import { compare, hash } from '../../deps.ts'; +} from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/user.model.ts'; +import { compareSync, hashSync } from '../../deps.ts'; import { generateToken } from '../utils/token.utils.ts'; import type { OptionalId } from '../../deps.ts'; const users = db.collection>('users'); const createUser = async (userInput: UserSignUpInput) => { - const existingUser = await users.findOne({ - $or: [{ email: userInput.email }, { username: userInput.username }], - }); + console.log('🍎Create user called'); + try { + console.log('🍎Checking for existing user...'); + const existingUser = await users.findOne({ + $or: [{ email: userInput.email }, { username: userInput.username }], + }); - if (existingUser) { - if (existingUser.email === userInput.email) { - throw new Error('User with this email already exists'); - } - if (existingUser.username === userInput.username) { - throw new Error('This username is already in use'); + if (existingUser) { + if (existingUser.email === userInput.email) { + throw new Error('User with this email already exists'); + } + if (existingUser.username === userInput.username) { + throw new Error('This username is already in use'); + } } + + console.log('🍎hashing password...'); + const passwordHash = hashSync(userInput.password); + // const passwordHash = compareSync(userInput.password, hash); + console.log('🍎Password hashed successfully'); + const userToInsert: NewUser = { + ...userInput, + password: passwordHash, + saved_events: [], + role: 'user', + }; + console.log('🍎User to insert:', { ...userToInsert, password: '[HIDDEN]' }); + + const result = await users.insertOne(userToInsert); + console.log('🍎User inserted into DB'); + if (!result) throw new Error('Failed to insert user'); + return result; + } catch (error) { + console.error('Error in create user:', error); + throw error; } - const passwordHash = await hash(userInput.password); - const userToInsert: NewUser = { - ...userInput, - password: passwordHash, - saved_events: [], - role: 'user', - }; - const result = await users.insertOne(userToInsert); - if (!result) throw new Error('Failed to insert user'); - return result; }; const logInUser = async (userInput: UserLogInInput) => { + console.log('🍏 Login user called'); + console.log('🍏 Finding user'); const exists = await users.findOne({ username: userInput.username }); + const validPassword = exists === null ? false - : await compare(userInput.password, exists.password); + : compareSync(userInput.password, exists.password); if (!(exists && validPassword)) { throw new Error('Invalid username or password'); } + console.log('🍏 User found'); const safeUser = toSafeUser(exists); const token = await generateToken(safeUser); + console.log('🍏 Returning user token'); return token; }; diff --git a/src/services/event.service.ts b/src/services/event.service.ts index db9d2ff..8c054f5 100644 --- a/src/services/event.service.ts +++ b/src/services/event.service.ts @@ -13,7 +13,7 @@ import { eventsArraySchema, eventSchema, FullEvent, -} from 'models/event.model.ts'; +} from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/event.model.ts'; import { normaliseEvents } from '../utils/event.utils.ts'; const events = db.collection('events'); @@ -32,7 +32,7 @@ const getEventById = async (id: string): Promise => { return await events.findOne({ _id: new ObjectId(id) }); }; -const updateEventById = async (id: string, event: FullEvent) => { +const updateEventById = async (id: string, event: Partial) => { return await events.updateOne({ _id: new ObjectId(id) }, { $set: event }); }; diff --git a/src/services/openai.service.ts b/src/services/openai.service.ts index 169df60..1372286 100644 --- a/src/services/openai.service.ts +++ b/src/services/openai.service.ts @@ -4,7 +4,7 @@ import { CompleteEventType, EventMode, eventsArraySchema, -} from 'models/event.model.ts'; +} from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/event.model.ts'; import { OpenAI, zodTextFormat } from '../../deps.ts'; import 'https://deno.land/std@0.224.0/dotenv/load.ts'; @@ -21,10 +21,11 @@ export const generateEvents = async ( category: EventMode[], location: string, ): Promise => { + console.log('Generating events'); const catString: string = category.join(' '); const userPrompt: string = - `List 5 events each for ${catString} near ${location}. Return only valid JSON in this format: + `Search for and list your top 75 interesting/fun/educational/charitable etc events within a 20km radius of ${location} (and don't be afraid to find the occasional obscure event!). Label each event as one of these categories to the best of your ability: ${catString}. Return only valid JSON in this format: { musicEvents: [ { diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 502575d..7dfdb93 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -5,11 +5,13 @@ import 'https://deno.land/std@0.224.0/dotenv/load.ts'; import { db } from '../database/connect.ts'; -import { UserInDB } from 'models/user.model.ts'; -import { hash, OptionalId } from '../../deps.ts'; -import { NewUser } from 'models/user.model.ts'; +import { UserInDB } from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/user.model.ts'; +import { hashSync, ObjectId, OptionalId } from '../../deps.ts'; +import { NewUser } from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/user.model.ts'; +import { FullEvent } from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/event.model.ts'; const users = db.collection>('users'); +const events = db.collection('events'); const getAllUsers = async ( role: 'user' | 'admin' | 'all', @@ -18,12 +20,8 @@ const getAllUsers = async ( return await users.find(query).toArray(); }; -export const userService = { - getAllUsers, -}; - const _createAdmin = async () => { - const passwordHash = await hash('supersecretadminpassword'); + const passwordHash = await hashSync('supersecretadminpassword'); const userToInsert: NewUser = { email: 'admin@example.com', username: 'admin', @@ -37,3 +35,39 @@ const _createAdmin = async () => { console.log('Admin created:', result); }; + +const handleUserEvents = async ( + eventId: string, + userId: string, + saving: boolean, +) => { + console.log(saving); + if (saving === true) { + await users.updateOne( + { _id: new ObjectId(userId) }, + { $addToSet: { saved_events: new ObjectId(eventId) } }, + ); + } else { + await users.updateOne( + { _id: new ObjectId(userId) }, + { $pull: { saved_events: new ObjectId(eventId) } }, + ); + } +}; + +const getUserEvents = async (userId: string) => { + const user = await users.findOne({ _id: new ObjectId(userId) }); + const savedEvents = user?.saved_events ?? []; + const eventsList = await events.find({ _id: { $in: savedEvents } }).toArray(); + return eventsList; +}; + +export const userService = { + getAllUsers, + handleUserEvents, + getUserEvents, +}; +/* +User clicks heart. Event target id? This is passed to backend via post request. Route grabs this. Calls this function via the +user controller. Save the id which should reference +*/ diff --git a/src/utils/event.utils.ts b/src/utils/event.utils.ts index 4253acf..34ba3ac 100644 --- a/src/utils/event.utils.ts +++ b/src/utils/event.utils.ts @@ -1,4 +1,8 @@ -import { Event, FrequencyObject, FullEvent } from 'models/event.model.ts'; +import { + Event, + FrequencyObject, + FullEvent, +} from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/event.model.ts'; // Initial cleaning of titles export const normalizeEventTitle = (event: Event): string => { diff --git a/supabase/.branches/_current_branch b/supabase/.branches/_current_branch new file mode 100644 index 0000000..88d050b --- /dev/null +++ b/supabase/.branches/_current_branch @@ -0,0 +1 @@ +main \ No newline at end of file diff --git a/supabase/.temp/cli-latest b/supabase/.temp/cli-latest new file mode 100644 index 0000000..eb41090 --- /dev/null +++ b/supabase/.temp/cli-latest @@ -0,0 +1 @@ +v2.24.3 \ No newline at end of file diff --git a/testing/dummyData/event.samples.ts b/testing/dummyData/event.samples.ts index 092730c..ef8dc9f 100644 --- a/testing/dummyData/event.samples.ts +++ b/testing/dummyData/event.samples.ts @@ -1,4 +1,4 @@ -import { Event } from 'models/event.model.ts'; +import { Event } from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/event.model.ts'; // Test event examples export const testEvents: Event[] = [ diff --git a/testing/dummyData/realData.samples.ts b/testing/dummyData/realData.samples.ts index 56da6fc..f4afb54 100644 --- a/testing/dummyData/realData.samples.ts +++ b/testing/dummyData/realData.samples.ts @@ -1,4 +1,4 @@ -import { Event } from 'models/event.model.ts'; +import { Event } from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/event.model.ts'; export const realData: Array = [ { diff --git a/testing/testScripts/event.utils.test.ts b/testing/testScripts/event.utils.test.ts index 3b28cbd..fa50db6 100644 --- a/testing/testScripts/event.utils.test.ts +++ b/testing/testScripts/event.utils.test.ts @@ -1,4 +1,7 @@ -import { FrequencyObject, FullEvent } from 'models/event.model.ts'; +import { + FrequencyObject, + FullEvent, +} from 'https://raw.githubusercontent.com/fac-31/Pro0428-LocalEventShared/main/src/models/event.model.ts'; import { realData } from '../dummyData/realData.samples.ts'; import { addNormalizedProperties,