Skip to content

Finish the half-built features: applications, saved resumes, entry removal, authenticated chat - #10

Open
shrish-browserstack wants to merge 8 commits into
ShrishRajGupta:abhishekfrom
shrish-browserstack:feat/finish-features
Open

shrish-browserstack wants to merge 8 commits into
ShrishRajGupta:abhishekfrom
shrish-browserstack:feat/finish-features

Conversation

@shrish-browserstack

Copy link
Copy Markdown

What

Stacked on the design-system PR. The last roadmap item: make the features the README already promises actually work, using the existing stack.

Applications (previously the form only console.logged and dropped the file)

  • Application model: post, applicant, employer, name/email/phone, cover note, resume URL, status. Unique per post and applicant.
  • POST /user/posts/:id/apply (multipart; optional PDF/Word resume to Cloudinary), GET /user/posts/:id/applicants (poster only), GET /user/applications, GET /user/posts/:id, applicant count. Photo and resume uploads share one multer middleware (5 MB, type-checked, temp file always removed).
  • Client: apply page prefilled from the session with the job summary at the top, "Your applications" page with status chips, applicants page for the poster (profile link, mailto/tel, resume link), "View applicants" on own post cards, account-menu entries for posts and applications.

Resume builder (previously lost everything on navigation)

  • GET/PUT /in/resume store the builder state on the user (size-capped). The builder loads it on mount, saves 800 ms after edits stop, shows Saving/Saved/Not saved, and does not save the untouched defaults.
  • Editor: form reflects externally loaded data without switching sections; "add new" no longer mutates state in place and no longer throws on an empty list; deleting keeps the index in range; list keys are stable.

Profile

  • DELETE /in/education|workEx|skills/:entryId; the Remove controls on own entries work and update in place.

Chat

  • The socket server verifies the API's httpOnly auth cookie on the handshake (shared ACCESS_TOKEN), so presence and delivery are keyed by a verified user id; a reconnect replaces the stale socket id instead of leaving messages undeliverable; conversationId is forwarded. The client connects withCredentials and no longer announces its own id.
  • GET /conversations/:userId returns populated participants, removing the per-row GET /user/:id request from the sidebar.

Also: feed pagination (page, limit, total), README updates (socket needs ACCESS_TOKEN).

Verification

  • server/scripts/security-smoke.sh extended to 73 checks; 73/73 pass against a fresh database (own-post apply 400, duplicate 409, third-party applicants 403, resume round-trip, entry removal, participants present).
  • Socket: anonymous connection rejected with "Authentication required"; cookie-authenticated connection accepted and shown in presence.
  • Browser, as a logged-in user: apply to another user's post → lands on "Your applications" with the entry → own post's "View applicants" shows the other user's application with contact links → remove an education entry in place → type a resume name, reload, both preview and editor show it → a live message from the other user (authenticated socket) appears in the open thread.
  • npm run lint: 0 warnings. CI=true npm run build: compiles.

🤖 Generated with Claude Code

shrish-browserstack and others added 8 commits September 13, 2026 02:58
- Remove gui.py and mode.pt (a 14.5 MB PyTorch model) — leftover
  artifacts from an unrelated YOLOv5 project, unused here.
- Delete client/src/img/home.png (11 MB, 6000x4000) — not referenced
  anywhere in the client.
- Remove the junk root package.json / package-lock.json (no name, no
  scripts, unrelated deps); the real apps live in client/ and server/.
- Drop the server's placeholder dependencies http (0.0.1-security),
  path, and url. All three are Node built-ins that shadow these
  userland packages, and http@0.0.1-security is a security-hold stub
  that blocked npm install.

Removes ~25 MB from the working tree. Verified: server npm install now
succeeds and the client production build compiles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Root package.json orchestrator: install:all, dev (client+server+socket via concurrently), build, lint
- .env.example for server, client and socket; socket reads PORT/CLIENT_URL from env
- GitHub Actions CI: install, lint, client build on Node 20
- .nvmrc, engines, .mailmap; plain Node .gitignore
- Fix socket missing nodemon dep, move server nodemon to devDependencies, plain `node` start scripts
- README rewritten around the three processes and correct .env locations; project-specific client README

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…password hashes

- validateJWT: return 401 (once) when no token, accept Authorization: Bearer as well as the cookie, remove the req.isAuthenticated bypass
- userModel: password select:false + toJSON transform; login selects +password explicitly
- /in/update|addCollege|addWorkEx|addSkills|add now require auth and act on req.user, never the URL or body; basic input validation; remove POST /in/populate
- conversations/messages: require auth, only members can read or post, sender comes from the token
- /email/intro: recipient is the logged-in user, not the request body; pooled transporter; respond after send
- cookie: sameSite=lax, maxAge=24h, secure in production; single CORS registration bound to CLIENT_URL with credentials
- search: escape user input before $regex, cap result sizes
- multer: 5 MB limit, reject non-images with 400, delete temp file after Cloudinary upload
- client: seven calls to the now-protected routes use relative paths so the cookie travels through the CRA proxy
- server/scripts/security-smoke.sh: 30-check curl suite used to verify the above

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… no-ops

Structure
- Four overlapping routers become six by domain: auth, users, profile, posts, chat, email
  (paths unchanged so the current client keeps working); app.js holds wiring, index.js boots
- config/env.js loads .env once (relative to server/, not cwd) and validates required keys;
  optional features (Cloudinary, Gmail) warn instead of failing at request time
- asyncHandler + global JSON error handler (Mongoose validation/cast -> 400, duplicate -> 409,
  multer -> 400) + JSON 404; unhandledRejection/uncaughtException exit; DB connect failure exits
- Drop the dead embedded socket.io server, view-engine config, body-parser; remove
  passport, uuid, body-parser, socket.io dependencies

Correctness
- createProfile wrote capitalised keys the schema silently dropped; now maps to about/education/
  workexperience/skills with {new:true}
- Friend requests: accept/reject pulls by sub-document _id (previously never removed), recipient
  must be the caller, $addToSet prevents duplicate friends, duplicate/self requests -> 409/400
- Restore GET /user/allposts (route was commented out; fell through to /user/:id and hung)
- Shadowed routes removed: second GET /search/:value, second GET /user/:friend_id, /user/mail stub
- Search returns users under `user` and posts under `posts` instead of one mixed array
- createPost validates required fields, handles a missing owner, returns the created post
- Register checks username as well as email (409 instead of a raw duplicate-key 500);
  default avatar is the bundled placeholder instead of a Wikimedia photo of a real person
- Models: email unique/lowercase, JobPost (was BlogDB; collection pinned) with timestamps and
  a user_id index, Message/Conversation fields required with indexes and refs

security-smoke.sh extended from 30 to 53 checks; all pass against a fresh database.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ero-warning build

Auth and routing
- src/api/client.js: one axios instance (REACT_APP_API_URL, withCredentials, 401 -> logout);
  per-domain modules api/auth|users|posts|chat replace 29 inline calls and 11 hardcoded
  localhost URLs; the malformed axios.get(url, null, cfg) search call is gone
- AuthContext stores the user object (not the login envelope), re-validates a cached session
  against GET /user/profile on load, exposes login/register/logout/updateUser/refreshUser
- ProtectedRoute/AnonymousRoute + AppLayout render the navbar once; unauthenticated visits
  redirect to /user/login and return to the requested page after login; catch-all 404
- Retire the isLogged/username/token localStorage keys, the js-cookie wrapper and logout.jsx;
  registration logs the user in directly (server already sets the cookie)
- createRoot + StrictMode; react-hot-toast mounted so failures are visible

Screens
- Dashboard: one fetch per resource (was two identical profile fetches + N+1 per friend),
  loading/error states, edits update state instead of window.location.reload()
- Profile: connect button hidden on own profile, "Connected"/"Request sent" states, isFriend
  derived from the session user (was true if the viewer had any friend at all)
- Home: friend requests answered via API and reflected immediately, no DOMContentLoaded hack,
  own posts have no Apply button; UserWidget reads the session user (its request hit a
  non-existent route) and shows real counts instead of demo copy
- Navbar: 300 ms debounced search, real pending-request badge, working Home/Resume/Messages
  buttons, logout via context
- Messenger: socket URL from env, disconnect/off on unmount, persist-then-emit with
  conversationId, keys on lists; ChatOnline creates a conversation when none exists
- Job form handles the 201 response; register clears the password-mismatch validity

Cleanup
- Delete 12 dead files and 12 unused dependencies (bootstrap, react-redux, js-cookie, ...)
- ResumeBuilder: hoist section constants, derive editor values from live information, real
  mailto:/tel:/https links, default case
- ESLint at --max-warnings=0 (was 64 warnings); CI build now runs with CI=true
- server: email route returns 503 when Gmail is unconfigured; error handler keeps messages
  of deliberate 5xx

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…red primitives

Structure (git mv, history preserved)
- src/pages/<Name>/<Name>Page.jsx for every routed screen (Landing, Login, Register,
  CreateProfile, Feed, PostJob, Profile, MyPosts, Messenger, Apply, ResumeBuilder, NotFound)
- src/features/{profile,chat,jobs} for domain components; src/components for shared primitives
  (Navbar, ModalBox, HeroLayout); widgets/ and css/ folded away
- PascalCase file names, component names match file names, one component per file
  (userExp.jsx split into WorkExperienceSection + SkillsSection)
- CSS co-located with its owner: home.css split into FeedPage.css / PostCard.css /
  HeroLayout.css, userdashboard.css into ProfilePage.css / ProfileCard.css, navbar +
  SearchResult + SearchResultsList merged into Navbar.css; dead .notification-list /
  .loading / .spinner rules dropped

De-duplication
- One PostCard (three drifting copies), one ModalBox (three verbatim modal style objects),
  one HeroLayout (hero markup pasted into landing, login and register)

Resume builder
- components/ResumeBuilder/App.js -> pages/ResumeBuilder/ResumeBuilderPage.jsx; its global
  `*` reset and .App layout rule move to src/styles/base.css, loaded from index.js on
  purpose instead of leaking from a feature stylesheet

No behaviour or visual change intended; lint 0 warnings, CI build passes, screens verified
in the browser.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
System
- src/styles/tokens.css: one palette (indigo accent on a warm off-white ground), radius,
  shadow, spacing and type scales as CSS custom properties; src/theme.js maps the same values
  into the MUI theme; ThemeProvider + CssBaseline in index.js; Manrope via Google Fonts
- src/styles/base.css: reset, typography, native button/input/textarea baselines (MUI
  elements excluded by class), focus-visible ring, .page / .form-card / .page-status
  primitives, reduced-motion guard
- Removed the global element selectors that leaked from page stylesheets (a, h2, form,
  label, input, button, blockquote, input[type=text]) and the copied button galleries

Screens
- Hero: indigo poster instead of a blurred stock photo; landing is a typographic poster with
  brand, headline, one line and two actions; login/register are a centred card
- Feed and profile pages use CSS grid and stack below 900/960px; messenger is a three-column
  grid with a real height (calc(100vh - nav), the old calc(100vh-70px) was invalid) that
  collapses to one column on phones
- PostCard shows title, company, meta chips and formatted salary; hover lift
- Profile card: identity block, action row, education/work/skills as panels (skills as chips),
  empty-state copy; dialogs use labelled fields
- Summary card with cover, avatar and stat row; connection-request card; message bubbles
- Navbar wrapper no longer flex-grows into the page (short pages had content pushed down)

All 30+ ad-hoc colours and every inline style outside the resume builder are gone.
Verified in the browser at 1280px and 400px: landing, login, profile, feed, messenger.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…icated chat socket

Applications (the README's headline feature had no backend)
- Application model (post, applicant, employer, contact details, cover note, resume URL,
  status; unique per post+applicant)
- POST /user/posts/:id/apply (multipart, optional PDF/Word resume to Cloudinary),
  GET /user/posts/:id/applicants (owner only), GET /user/applications, GET /user/posts/:id,
  applicant count; shared upload middleware for photos and resumes
- Client: apply page prefilled from the session, "Your applications" page with status,
  applicants page for the poster, "View applicants" on own post cards, menu entries

Resume builder
- GET/PUT /in/resume store the builder state on the user; the builder loads it on mount and
  saves after a pause in editing with a Saving/Saved indicator; the editor form now reflects
  externally loaded data; add-new no longer mutates state in place or crashes on an empty
  list; delete keeps the index in range; stable list keys

Profile
- DELETE /in/education|workEx|skills/:entryId; Remove controls on own profile entries

Chat
- Socket server verifies the API's auth cookie on the handshake (shared ACCESS_TOKEN),
  keys presence by user id so a reconnect replaces the stale socket, forwards conversationId;
  client connects withCredentials and no longer announces its own id
- GET /conversations/:userId returns populated participants, removing the per-row user request

Also: feed pagination (page/limit/total), 73-check smoke suite, README updates.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant