
- Updated `.env` and added `.env.test` for environment variables. - Introduced API documentation in `API_DOCUMENTATION.md`. - Added authentication setup guide in `AUTHENTICATION_SETUP.md`. - Implemented user authentication with JWT and email verification. - Created new routes for user management and form submissions. - Added middleware for API key authentication and error handling. - Set up Redis for rate limiting and notifications. - Removed obsolete files and configurations related to the previous Rust implementation.
21 KiB
Task 2.1: User Dashboard & Form Management UI (Replacing current "admin")
- Mindset Shift: This is no longer an admin panel. It's the user's control center.
Subtask 2.1.1: Design User Dashboard Layout
- Wireframe basic layout:
- Navigation Bar:
- Logo/App Name (e.g., "Formies")
- My Forms (Active Link)
- Create New Form
- Account Settings (e.g., "Hi, [User Name]" dropdown with "Settings", "Logout")
- Main Content Area (for "My Forms" view):
- Header: "My Forms"
- Button: "+ Create New Form"
- Forms List Table:
- Columns: Form Name, Submissions (count), Endpoint URL, Created Date, Actions
- Actions per row: View Submissions, Settings, Archive/Delete
- Pagination for the forms list if it becomes long.
- Main Content Area (for "Create New Form" view - initial thought, might be a separate page/modal):
- Header: "Create New Form"
- Form fields: Form Name
- Button: "Create Form"
- Main Content Area (for "Account Settings" - placeholder for now):
- Header: "Account Settings"
- Placeholder content.
- Navigation Bar:
- Frontend Tech Decision:
- EJS for templating, made dynamic with client-side JavaScript. This aligns with the existing structure and MVP scope. We will enhance EJS views to be more interactive.
[X] Wireframe basic layout: List forms, create form, account settings (placeholder). - Textual wireframe defined above [X] Decide on frontend tech (EJS is fine for MVP if you make it dynamic with client-side JS, or consider a simple SPA framework if comfortable). - Decision made: EJS with client-side JS
- Created
views/dashboard.ejs
as the main layout. - Created
views/partials/_forms_table.ejs
for displaying the list of forms.
Subtask 2.1.2: "My Forms" View:
- Objective: Fetch and display forms owned by the logged-in user.
- Show key info: name, submission count, endpoint URL, created date, status (Active/Archived).
- Links/Actions: View Submissions, Settings, Archive/Unarchive, Delete.
- Frontend:
views/dashboard.ejs
withview = 'my_forms'
andviews/partials/_forms_table.ejs
will handle this. - Backend:
- Need a new route, e.g.,
GET /dashboard
, protected by authentication (e.g.,requireAuth
fromauthMiddleware.js
). - This route handler will:
- Fetch forms for
req.user.id
from the database. - Query should include
name
,uuid
,created_at
,is_archived
, andsubmission_count
. - Render
views/dashboard.ejs
passing the forms data,user
object,appUrl
, andview = 'my_forms'
.
- Fetch forms for
- Implemented in
src/routes/dashboard.js
via GET/
.
- Need a new route, e.g.,
[X] Fetch and display forms owned by the logged-in user.
[X] Show key info: name, submission count, endpoint URL, created date.
[X] Links to: view submissions, edit settings, delete. (Links are present in _forms_table.ejs
, functionality for all to be built out in subsequent tasks)
Subtask 2.1.3: "Create New Form" Functionality (for logged-in user):
- UI:
dashboard.ejs
(withview = 'create_form'
) provides the form input.- Route
GET /dashboard/create-form
insrc/routes/dashboard.js
renders this view.
- Route
- Backend:
POST /dashboard/forms/create
route insrc/routes/dashboard.js
handles form submission.- Associates form with
req.user.id
. - Redirects to
/dashboard
on success. - Handles errors and re-renders create form view with an error message.
- Associates form with
[X] UI and backend logic. Associates form with req.user.id.
Subtask 2.1.4: "View Form Submissions" (Scoped & Paginated):
- Objective: Allow users to view submissions for their specific forms, with pagination.
- UI:
views/partials/_submissions_view.ejs
created to display submissions list and pagination.views/dashboard.ejs
updated to include this partial whenview = 'form_submissions'
.
- Backend:
- Route:
GET /dashboard/submissions/:formUuid
added tosrc/routes/dashboard.js
. - Verifies that
req.user.id
owns theformUuid
. - Fetches paginated submissions for the given
formUuid
. - Renders
dashboard.ejs
withview = 'form_submissions'
, passing submissions data, form details, and pagination info. - Error handling improved to render user-friendly messages within the dashboard view.
- Route:
[X] UI and backend for a user to view submissions for their specific form. [X] Pagination is critical here (as you have).
Subtask 2.1.5: Form Settings UI (Basic):
- Objective: Allow users to update basic form settings, starting with the form name.
- UI:
- A new view/section in
dashboard.ejs
(e.g., whenview = 'form_settings'
). - This view will display a form with an input for the form name.
- It will also be a placeholder for future settings (thank you URL, notifications).
- A new view/section in
- Backend:
- Route:
GET /dashboard/forms/:formUuid/settings
to display the settings page.- Implemented in
src/routes/dashboard.js
. - Verifies form ownership by
req.user.id
. - Fetches current form details (name).
- Renders the
form_settings
view indashboard.ejs
.
- Implemented in
- Route:
POST /dashboard/forms/:formUuid/settings/update-name
to handle the update.- Implemented in
src/routes/dashboard.js
. - Verifies form ownership.
- Updates the form name in the database.
- Redirects back to form settings page with a success/error message via query parameters.
- Implemented in
- Route:
[X] Allow users to update form name. [X] Placeholder for future settings (thank you URL, notifications) - (Placeholders added in EJS).
Subtask 2.1.6: Delete Form/Submission (with soft delete/archival consideration):
-
Objective: Implement form archival (soft delete) and permanent deletion for users.
-
Users should be able to archive/unarchive their forms.
-
True delete should be a confirmed, rare operation.
-
The
is_archived
field in theforms
table will be used. -
Submissions deletion is already partially handled in
_submissions_view.ejs
via a POST to/dashboard/submissions/delete/:submissionId
. We need to implement this backend route. -
Form Archival/Unarchival:
- UI: Buttons for "Archive" / "Unarchive" are already in
views/partials/_forms_table.ejs
.- Archive action:
POST /dashboard/forms/archive/:formUuid
- Unarchive action:
POST /dashboard/forms/unarchive/:formUuid
- Archive action:
- Backend:
- Create these two POST routes in
src/routes/dashboard.js
. - Must verify form ownership by
req.user.id
. - Fetch current form details (name).
- Render the settings view.
- Create these two POST routes in
- Route:
POST /dashboard/forms/:formUuid/settings
(or/dashboard/forms/:formUuid/update-name
) to handle the update.- Must verify form ownership.
- Update the form name in the database.
- Redirect back to form settings page or main dashboard with a success message.
- UI: Buttons for "Archive" / "Unarchive" are already in
- Submission Deletion (User-scoped):
- UI: "Delete" button per submission in
views/partials/_submissions_view.ejs
(withconfirm()
dialog).- Action:
POST /dashboard/submissions/delete/:submissionId
- Action:
- Backend (in
src/routes/dashboard.js
):- Implemented
POST /dashboard/submissions/delete/:submissionId
:- Verifies the
req.user.id
owns the form to which the submission belongs. - Deletes the specific submission.
- Redirects back to the form's submissions view (
/dashboard/submissions/:formUuid
) with message.
- Verifies the
- Implemented
- UI: "Delete" button per submission in
[X] You have is_archived. Solidify this. Users should be able to archive/unarchive. [X] True delete should be a confirmed, rare operation. [X] Implement user-scoped submission deletion.
Task 2.2: Per-Form Configuration by User
- Mindset Shift: Empower users to customize their form behavior.
Subtask 2.2.1: Database Schema Updates for forms Table:
- Objective: Add new fields to the
forms
table to support per-form email notification settings. - Review existing fields (
thank_you_url
,thank_you_message
,ntfy_enabled
,allowed_domains
) - these are good as per plan. - New fields to add:
email_notifications_enabled
(BOOLEAN, DEFAULT FALSE, NOT NULL)notification_email_address
(VARCHAR(255), NULL) - This will store an override email address. If NULL, the user's primary email will be used.
[X] Review existing fields (thank_you_url, thank_you_message, ntfy_enabled, allowed_domains). These are good.
[X] Add email_notifications_enabled (boolean). (Added to init.sql
)
[X] Add notification_email_address (string, defaults to user's email, but allow override). (Added to init.sql
)
Subtask 2.2.2: UI for Form Settings Page:
- Objective: Enhance the form settings page to allow users to configure these new email notification options.
- The existing form settings page is
dashboard.ejs
withview = 'form_settings'
(created in Subtask 2.1.5). - UI Elements to add to this page:
- Email Notifications Section:
- Checkbox/Toggle: "Enable Email Notifications for new submissions"
- Controls
email_notifications_enabled
.
- Controls
- Input Field (text, email type): "Notification Email Address"
- Controls
notification_email_address
. - Should be pre-filled with the user's primary email if
notification_email_address
is NULL/empty in the DB. - Label should indicate that if left blank, notifications will go to the account email.
- Controls
- Checkbox/Toggle: "Enable Email Notifications for new submissions"
- Email Notifications Section:
- The
GET /dashboard/forms/:formUuid/settings
route will need to fetch these new fields. - The form on this page will need to be updated to submit these new fields. The POST route will likely be
/dashboard/forms/:formUuid/settings/update-notifications
or similar, or a general update to the existing/dashboard/forms/:formUuid/settings/update-name
to become a general settings update route.
[X] Create a dedicated page/modal for each form's settings. (Using existing settings section in dashboard.ejs
)
[X] Allow users to edit: Name, Email Notification toggle, Notification Email Address. (Thank You URL, Thank You Message, Allowed Domains are placeholders for now as per 2.1.5).
_ UI elements added to dashboard.ejs
in the form_settings
view.
_ GET /dashboard/forms/:formUuid/settings
in src/routes/dashboard.js
updated to fetch and pass these settings. * POST /dashboard/forms/:formUuid/settings/update-notifications
in src/routes/dashboard.js
created to save these settings.
Subtask 2.2.3: Backend to Save and Apply Settings:
- Objective: Ensure the backend API endpoints correctly save and the submission logic uses these settings.
- API endpoints to update settings for a specific form (owned by user):
POST .../update-name
(Done in 2.1.5)POST .../update-notifications
(Done in 2.2.2)- Future: endpoints for Thank You URL, Message, Allowed Domains.
- Logic in
/submit/:formUuid
to use these form-specific settings:- When a form is submitted to
/submit/:formUuid
:- Fetch the form's settings from the DB, including
email_notifications_enabled
andnotification_email_address
. - This logic is now implemented in
src/routes/public.js
as part of Task 2.3.2 integration.
- Fetch the form's settings from the DB, including
- When a form is submitted to
[X] API endpoints to update these settings for a specific form (owned by user). (Name and Email Notification settings covered so far) [X] Logic in /submit/:formUuid to use these form-specific settings. (Addressed as part of 2.3.2)
Task 2.3: Email Notifications for Submissions (Core Feature)
- Mindset Shift: Ntfy is cool for you. Users expect email.
Subtask 2.3.1: Integrate Transactional Email Service:
- Objective: Set up a third-party email service to send submission notifications.
- Action for you (USER):
- Choose a transactional email service (e.g., SendGrid, Mailgun, AWS SES). Many offer free tiers.
- Sign up for the service and obtain an API Key.
- Securely store this API Key as an environment variable in your
.env
file.- For example, if you choose SendGrid, you might use
SENDGRID_API_KEY=your_actual_api_key
. - Also, note the sender email address you configure with the service (e.g.,
EMAIL_FROM_ADDRESS=notifications@yourdomain.com
).
- For example, if you choose SendGrid, you might use
- Once you have these, let me know which service you've chosen so I can help with installing the correct SDK and writing the integration code.
- User selected: Resend
- API Key ENV Var:
RESEND_API_KEY
- From Email ENV Var:
EMAIL_FROM_ADDRESS
[X] Sign up for SendGrid, Mailgun, AWS SES (free tiers available). (User selected Resend)
[X] Install their SDK. (npm install resend done)
[X] Store API key securely (env vars). (User confirmed RESEND_API_KEY
and EMAIL_FROM_ADDRESS
are set up)
Subtask 2.3.2: Email Sending Logic:
- Objective: Create a reusable service/function to handle the sending of submission notification emails.
- This service will use the Resend SDK and the configured API key.
- Create a new service file:
src/services/emailService.js
- It should export a function, e.g.,
sendSubmissionNotification(form, submissionData, userEmail)
.form
: An object containing form details (name
,email_notifications_enabled
,notification_email_address
).submissionData
: The actual data submitted to the form.userEmail
: The email of the user who owns the form (to be used ifform.notification_email_address
is not set).
- Inside the function:
- Check if
form.email_notifications_enabled
is true. - Determine the recipient:
form.notification_email_address
oruserEmail
. - Construct the email subject and body (using a basic template for now - Subtask 2.3.3).
- Use the Resend SDK to send the email.
- Include error handling (Subtask 2.3.4).
- Check if
- It should export a function, e.g.,
[X] Create a service/function sendSubmissionNotification(form, submissionData, userEmail) - (src/services/emailService.js
created with this function).
[X] If email_notifications_enabled for the form, send an email to notification_email_address (or user's email). - (Logic implemented in emailService.js
and integrated into /submit/:formUuid
route in src/routes/public.js
).
Subtask 2.3.3: Basic Email Template:
- Objective: Define a simple, clear email template for notifications.
- The current
createEmailHtmlBody
function insrc/services/emailService.js
provides a very basic HTML template:- Subject: "New Submission for [Form Name]"
- Body: Lists submitted data (key-value pairs).
- This fulfills the MVP requirement.
[X] Simple, clear email: "New Submission for [Form Name]", list submitted data. (Implemented in emailService.js
)
Subtask 2.3.4: Error Handling for Email Sending:
- Objective: Ensure email sending failures don't break the submission flow and are logged.
- In
src/services/emailService.js
, withinsendSubmissionNotification
:- Errors from
resend.emails.send()
are caught and logged. - The function does not throw an error that would halt the caller, allowing the submission to be considered successful even if the email fails.
- Errors from
- In
src/routes/public.js
(/submit/:formUuid
route):- The call to
sendSubmissionNotification
is followed by.catch()
to log any unexpected errors from the email sending promise itself, ensuring the main response to the user is not blocked.
- The call to
[X] Log errors if email fails to send; don't let it break the submission flow. (Implemented in emailService.js
and public.js
route)
Task 2.4: Enhanced Spam Protection (Beyond Basic Honeypot)
- Mindset Shift: Your honeypot is step 1. Real services need more.
Subtask 2.4.1: Integrate CAPTCHA (e.g., Google reCAPTCHA):
-
Objective: Add server-side CAPTCHA validation to the form submission process.
-
We'll use Google reCAPTCHA v2 ("I'm not a robot" checkbox) for this MVP.
-
Action for you (USER):
- Go to the Google reCAPTCHA admin console.
- Register your site: Choose reCAPTCHA v2, then "I'm not a robot" Checkbox.
- Add your domain(s) (e.g.,
localhost
for development, and your production domain). - Accept the terms of service.
- You will receive a Site Key and a Secret Key.
- Store these securely in your
.env
file:RECAPTCHA_V2_SITE_KEY=your_site_key
RECAPTCHA_V2_SECRET_KEY=your_secret_key
-
Let me know once you have these keys set up in your
.env
file. -
Frontend Changes (Illustrative - User will implement on their actual forms):
- User needs to include the reCAPTCHA API script in their HTML form page:
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
- User needs to add the reCAPTCHA widget div where the checkbox should appear:
<div class="g-recaptcha" data-sitekey="YOUR_RECAPTCHA_V2_SITE_KEY"></div>
(replacing with the actual site key, possibly passed from server or configured client-side if site key is public).
- User needs to include the reCAPTCHA API script in their HTML form page:
-
Backend Changes (
/submit/:formUuid
route insrc/routes/public.js
):- When a submission is received, it should include a
g-recaptcha-response
field from the reCAPTCHA widget. - Create a new middleware or a helper function
verifyRecaptcha(recaptchaResponse, clientIp)
.- This function will make a POST request to Google's verification URL:
https://www.google.com/recaptcha/api/siteverify
. - Parameters:
secret
(yourRECAPTCHA_V2_SECRET_KEY
),response
(theg-recaptcha-response
value),remoteip
(optional, user's IP). - The response from Google will be JSON indicating success or failure.
- This function will make a POST request to Google's verification URL:
- In the
/submit
route, call this verification function. If verification fails, reject the submission with an appropriate error.
- When a submission is received, it should include a
[X] Sign up for reCAPTCHA (v2 "I'm not a robot" or v3 invisible). Get site/secret keys. (User action) - User confirmed keys are in .env
[ ] Frontend: Add reCAPTCHA widget/JS to user's HTML form example. (User responsibility for their forms)
[X] Backend: /submit/:formUuid endpoint must verify reCAPTCHA token with Google. (Already implemented in src/routes/public.js
using src/utils/recaptchaHelper.js
)
Subtask 2.4.2: User Configuration for Spam Protection:
-
Database Schema: Add
recaptcha_enabled
(BOOLEAN, DEFAULT FALSE) toforms
table. (Done ininit.sql
) -
UI: Added reCAPTCHA toggle to Form Settings page (
dashboard.ejs
) and consolidated settings form to POST to/dashboard/forms/:formUuid/settings/update
. (Done) -
Backend:
GET /dashboard/forms/:formUuid/settings
fetches and passesrecaptcha_enabled
. (Done)- Consolidated
POST /dashboard/forms/:formUuid/settings/update
savesrecaptcha_enabled
and other settings (formName, emailNotificationsEnabled, notificationEmailAddress). (Done) /submit/:formUuid
inpublic.js
now checks form'srecaptcha_enabled
flag: if true, token is required & verified; if false, check is skipped. (Done)
-
Allow users to enable/disable reCAPTCHA for their forms (and input their own site key if they want, or use a global one you provide). - Implemented using global keys for MVP.
-
Subtask 2.4.3: (Future Consideration) Akismet / Content Analysis.
Task 2.5: Basic API for Users to Access Their Data
- Mindset Shift: Power users and integrations need an API.
Subtask 2.5.1: API Key Generation & Management:
- Objective: Allow users to generate/revoke API keys from their dashboard.
- Action for you (USER):
- Choose a RESTful API framework (e.g., Express, Fastify).
- Implement the API endpoints to allow users to access their data.
- Ensure the API is secure and uses authentication.
- Let me know once you have the API implemented and tested.
[X] Database Schema: Create api_keys
table (userid, key_name, api_key_identifier, hashed_api_key_secret, etc.). (_Done in init.sql
with refined structure)
[X] Helper Utilities: Created src/utils/apiKeyHelper.js
with generateApiKeyParts
, hashApiKeySecret
, compareApiKeySecret
. (Done)
[X] Backend Routes: Added GET /dashboard/api-keys
(list), POST /dashboard/api-keys/generate
(create), POST /dashboard/api-keys/:apiKeyUuid/revoke
(delete) to src/routes/dashboard.js
. (Done)
[X] UI in Dashboard: Added "API Keys" section to dashboard.ejs
for generating, listing (name, identifier, created/lastused), and revoking keys. Displays newly generated key once via session. (_Done)
[X] Allow users to generate/revoke API keys from their dashboard. (Done)
[X] Store hashed API keys in DB, associated with user. (Done via backend routes and helpers)
Subtask 2.5.2: Secure API Endpoints:
- Objective: Ensure the API is secure and uses authentication.
- Action for you (USER):
- Choose a RESTful API framework (e.g., Express, Fastify).
- Implement the API endpoints to allow users to access their data.
- Ensure the API is secure and uses authentication.
- Let me know once you have the API implemented and tested.
[X] Created src/middleware/apiAuthMiddleware.js
for Bearer token authentication (checks signature, expiry, active user, updates lastused). (_Done)
[X] Created src/routes/api_v1.js
and mounted it at /api/v1
in server.js
. (Done)
[X] Added GET /api/v1/forms
(list user's forms) and GET /api/v1/forms/:formUuid/submissions
(list form submissions, paginated), both protected by the API auth middleware. (Done)
[X] Create new API routes (e.g., /api/v1/forms, /api/v1/forms/:uuid/submissions). (Covered by above point)
[X] Authenticate using API keys (e.g., Bearer token). (Done)
Subtask 2.5.3: Basic API Documentation:
- Objective: Provide basic documentation for the API.
- Action for you (USER):
- Choose a documentation format (e.g., Swagger, Postman, Markdown).
- Implement the documentation for the API endpoints.
- Let me know once you have the API documentation implemented.
[ ] Simple Markdown file explaining authentication and available endpoints.