Skip to content

Commit

Permalink
Merge pull request #123 from protob/template/vue-express-fullstack
Browse files Browse the repository at this point in the history
feat:[TEMPLATE] - Vue + Express - FullStack #96
  • Loading branch information
Abhishek-Mallick authored Oct 8, 2024
2 parents 43f325c + 8e7512b commit 9e4f9ba
Show file tree
Hide file tree
Showing 34 changed files with 1,155 additions and 0 deletions.
133 changes: 133 additions & 0 deletions template/FullStack/Vue(Frontend)+Express(Backend)/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# FullStack Template: Vue.js + Express.js

This repository provides a simple FullStack template for building modern web applications using **Vue.js** for the frontend and **Express.js** for the backend. It includes essential features such as user authentication, session management, and an user interface.

## Features

- **User Authentication**: Supports user registration and login with session management using JWT.
- **Frontend**: Built with Vue.js, offering a dynamic and responsive user interface.
- **Backend**: Powered by Express.js with Node.js, providing a robust RESTful API.
- **Session Management**: Utilizes JSON Web Tokens (JWT) to manage user authentication.

## Technologies Used

### Frontend

- **Vue.js 3**: A progressive JavaScript framework for building user interfaces, using the Composition API.
- **Vue Router**: Handles navigation and routing in the application.
- **Pinia**: State management library for Vue.js applications.
- **Tailwind CSS**: Utility-first CSS framework for rapid UI development.
- **Vue Toastification**: For displaying toast notifications.
- **@vueuse/motion**: For improved animation experience.

### Backend

- **Node.js**: A JavaScript runtime built on Chrome's V8 JavaScript engine.
- **Express.js**: A minimal and flexible Node.js web application framework.
- **MongoDB**: A NoSQL database known for its flexibility and scalability.
- **Mongoose**: An elegant MongoDB object modeling for Node.js.

## Installation

### Prerequisites

- **Node.js**: Ensure you have Node.js installed. You can download it from [here](https://nodejs.org/).
- **MongoDB**: Make sure MongoDB is installed and running locally or accessible remotely.

### Steps

1. **Populate the `.env.example` File**:

```bash
# server/.env
MONGO_URL=<YOUR_MONGODB_CONNECTION_STRING>
JWT_SECRET=<YOUR_JWT_SECRET_KEY>
PORT=3000
```

2. **Install the Required Dependencies**:

```bash
cd client && npm install
cd server && npm install
```

3. **Ensure MongoDB is Running**:
- Make sure your MongoDB server is running locally or accessible remotely.

4. **Run the Application**:

```bash
# both client and server
npm run dev
```

5. **Access the Application**:

Visit `http://localhost:5173/` in your web browser.

## Routes and Functionalities

- **`/api/auth/signup` [POST]**:
- Handles user registration.
- **Request Body**:
- `username`: String
- `emailid`: String
- `password`: String


- **`/api/auth/signin` [POST]**:
- **Description**: Handles user login.
- **Request Body**:
- `emailid`: String
- `password`: String

- **`/api/user/signout` [POST]**:
- Logs the user out by clearing the authentication cookie.
- **`/api/user/test` [GET]**:
- **Description**: Test route to verify API is working.

- **`/api/user/profile` [GET]**:
- **Description**: Retrieves the user profile information of the authenticated user.
- **Response**:
- **Success**: Returns user profile details such as `id`, `username`, and `emailid`.
- **Error**: Returns an error if the user is not authenticated.



### Frontend Routes

- **`/` [GET]**:
- **Description**: Renders the homepage of the application.

- **`/signin` [GET]**:
- Renders the login page where users can log in with their credentials.

- **`/signup` [GET]**:
- Renders the registration page where new users can sign up.

## Flash Messages

The application uses flash messages to communicate the following events to the user:

- **Signup**:
- **Success**: "Signup successful! Please sign in."
- **Error**: "Username or Email already exists. Please choose a different one."

- **Signin**:
- **Error**: "Invalid email or password. Please try again."

These messages are displayed on the frontend in the registration and login pages using toast notifications.

## Database

The application uses **MongoDB** for storing user information. The `users` collection in the MongoDB database contains the following fields for each user:

- **_id**: `ObjectId`, the unique identifier for each document (user).
- **username**: `String`, unique, cannot be null.
- **emailid**: `String`, unique, cannot be null.
- **password**: `String`, hashed, cannot be null.

---

Made using [Universal-Box](https://github.com/Abhishek-Mallick/universal-box)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_BASE_URL=http://localhost:PORT
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "vue-frontend",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
"lint": "eslint ."
},
"dependencies": {
"@vueuse/motion": "^2.2.5",
"axios": "^1.4.0",
"pinia": "^2.1.0",
"pinia-plugin-persistedstate": "^4.1.1",
"vue": "^3.3.4",
"vue-router": "^4.2.3",
"vue-toastification": "next"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.4",
"vite": "^5.4.8",
"autoprefixer": "^10.4.7",
"eslint": "^8.28.0",
"postcss": "^8.4.14",
"tailwindcss": "^3.4.10"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<div class="flex flex-col min-h-screen min-w-[320px]">
<Header />
<main class="flex-grow flex items-center justify-center">
<router-view class="w-full" />
</main>
<Footer />
</div>
</template>

<script setup>
import Header from './components/Header.vue'
import Footer from './components/Footer.vue'
</script>

<style>
body, html {
height: 100%;
margin: 0;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import axios from 'axios'
const baseURL = import.meta.env.VITE_BASE_URL || 'http://localhost:3000'

const api = axios.create({
baseURL,
withCredentials: true, // Ensure credentials/cookies are sent with cross-origin requests
})

// Set up a response interceptor to handle errors globally
api.interceptors.response.use(
(response) => response, // return the response if no errors

(error) => {
// Check and reject 401 (Unauthorized) error
if (error.response && error.response.status === 401) {
return Promise.reject(error)
}
console.error('API Error:', error)
return Promise.reject(error)
}
)

export default api
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<template>
<div class="max-w-5xl mx-auto px-0 xs:px-8">
<CardsListingHover :items="projects" />
</div>
</template>

<script setup>
import CardsListingHover from './CardsListingHover.vue'
const projects = [
{
title: "Stripe",
description: "A technology company that builds economic infrastructure for the internet.",
link: "stripe.com",
},
{
title: "Netflix",
description: "A streaming service that offers a wide variety of award-winning TV shows, movies, anime, documentaries, and more on thousands of internet-connected devices.",
link: "netflix.com",
},
{
title: "Google",
description: "A multinational technology company that specializes in Internet-related services and products.",
link: "google.com",
},
{
title: "Meta",
description: "A technology company that focuses on building products that advance Facebook's mission of bringing the world closer together.",
link: "meta.com",
},
{
title: "Amazon",
description: "A multinational technology company focusing on e-commerce, cloud computing, digital streaming, and artificial intelligence.",
link: "amazon.com",
},
{
title: "Microsoft",
description: "A multinational technology company that develops, manufactures, licenses, supports, and sells computer software, consumer electronics, personal computers, and related services.",
link: "microsoft.com",
},
]
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<template>
<div class="max-w-5xl mx-auto md:px-8">
<div :class="['grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 py-10 cursor-pointer', className]">
<div
v-for="(item, idx) in items"
:key="item.link"
class="relative group block p-2 h-full w-full"
@mouseenter="setHoveredIndex(idx)"
@mouseleave="setHoveredIndex(null)"
@click="navigate(item.link)"
>
<div
v-motion
:initial="{ opacity: 0, scale: 0.95 }"
:enter="{
opacity: 1,
scale: .99,
transition: {
type: 'spring',
stiffness: 300,
damping: 30,
opacity: { duration: 0.2 }
}
}"
:leave="{
opacity: 0,
scale: 0.95,
transition: {
type: 'spring',
stiffness: 300,
damping: 30,
opacity: { duration: 0.2 }
}
}"
:style="{
position: 'absolute',
inset: '-5px',
zIndex: 0,
pointerEvents: 'none'
}"
class="bg-neutral-200 dark:bg-slate-800/[0.8] rounded-3xl"
v-if="hoveredIndex === idx"
></div>
<div class="rounded-2xl h-full w-full p-4 overflow-hidden bg-black border border-transparent dark:border-white/[0.2] relative z-10">
<div class="p-4">
<h4 class="text-zinc-100 font-bold tracking-[1px] mt-4 uppercase text-center">
{{ item.title }}
</h4>
<p class="mt-8 text-zinc-400 tracking-wide leading-relaxed text-sm text-center">
{{ item.description }}
</p>
</div>
</div>
</div>
</div>
</div>
</template>

<script setup>
import { ref } from 'vue'
const props = defineProps({
items: {
type: Array,
required: true
},
className: {
type: String,
default: ''
}
})
const hoveredIndex = ref(null)
const setHoveredIndex = (idx) => {
hoveredIndex.value = idx
}
const navigate = (link) => {
const url = link.startsWith('https://') ? link : `https://${link}`
window.open(url, '_blank', 'noopener,noreferrer')
}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<template>
<footer class="bg-gray-100 p-5 flex justify-center items-center">
<div class="flex items-center gap-2">
<p class="text-sm text-gray-600">© 2024 Universal-Box</p>
</div>
</footer>
</template>

<script>
export default {
name: 'Footer'
}
</script>
Loading

0 comments on commit 9e4f9ba

Please sign in to comment.