Appwrite auth
This commit is contained in:
commit
24a979b364
24
frontend/.gitignore
vendored
Normal file
24
frontend/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Nuxt dev/build outputs
|
||||
.output
|
||||
.data
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache
|
||||
dist
|
||||
|
||||
# Node dependencies
|
||||
node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.fleet
|
||||
.idea
|
||||
|
||||
# Local env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
75
frontend/README.md
Normal file
75
frontend/README.md
Normal file
@ -0,0 +1,75 @@
|
||||
# Nuxt Minimal Starter
|
||||
|
||||
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
||||
|
||||
## Setup
|
||||
|
||||
Make sure to install dependencies:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm install
|
||||
|
||||
# pnpm
|
||||
pnpm install
|
||||
|
||||
# yarn
|
||||
yarn install
|
||||
|
||||
# bun
|
||||
bun install
|
||||
```
|
||||
|
||||
## Development Server
|
||||
|
||||
Start the development server on `http://localhost:3000`:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run dev
|
||||
|
||||
# pnpm
|
||||
pnpm dev
|
||||
|
||||
# yarn
|
||||
yarn dev
|
||||
|
||||
# bun
|
||||
bun run dev
|
||||
```
|
||||
|
||||
## Production
|
||||
|
||||
Build the application for production:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run build
|
||||
|
||||
# pnpm
|
||||
pnpm build
|
||||
|
||||
# yarn
|
||||
yarn build
|
||||
|
||||
# bun
|
||||
bun run build
|
||||
```
|
||||
|
||||
Locally preview production build:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run preview
|
||||
|
||||
# pnpm
|
||||
pnpm preview
|
||||
|
||||
# yarn
|
||||
yarn preview
|
||||
|
||||
# bun
|
||||
bun run preview
|
||||
```
|
||||
|
||||
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
30
frontend/app.vue
Normal file
30
frontend/app.vue
Normal file
@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div class="flex min-h-screen bg-gray-100 text-gray-900">
|
||||
<!-- Sidebar and Navbar only for authenticated & non-public routes -->
|
||||
<Sidebar v-if="!isPublicPage" />
|
||||
|
||||
<div v-if="!isPublicPage" class="flex flex-col flex-1 ml-64">
|
||||
<Navbar />
|
||||
<main class="p-6">
|
||||
<NuxtPage />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Auth pages like SignIn or SignUp show full page -->
|
||||
<div v-else class="w-full">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRoute } from 'vue-router'
|
||||
import { computed } from 'vue'
|
||||
import Sidebar from '~/app/components/Sidebar.vue'
|
||||
import Navbar from '~/app/components/Navbar.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const publicRoutes = ['/', '/SignIn', '/SignUp'] // Add more if needed
|
||||
|
||||
const isPublicPage = computed(() => publicRoutes.includes(route.path))
|
||||
</script>
|
1
frontend/app/assets/style.css
Normal file
1
frontend/app/assets/style.css
Normal file
@ -0,0 +1 @@
|
||||
@import "tailwindcss";
|
35
frontend/app/components/Navbar.vue
Normal file
35
frontend/app/components/Navbar.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<header class="bg-white shadow p-4 flex justify-between items-center">
|
||||
<h2 class="text-xl font-semibold">Welcome, Atul</h2>
|
||||
<div class="flex items-center space-x-4">
|
||||
<span class="text-gray-700">🔔 Notifications</span>
|
||||
<span class="text-gray-700">👤 Profile</span>
|
||||
<button @click="logout" class="bg-red-600 text-white px-4 py-2 rounded-md hover:bg-red-400">
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Client, Account } from 'appwrite'
|
||||
|
||||
// Appwrite client setup
|
||||
const client = new Client()
|
||||
client.setEndpoint('https://cloud.appwrite.io/v1').setProject('67e1445400053dca1d9b')
|
||||
const account = new Account(client)
|
||||
|
||||
// Router
|
||||
const router = useRouter()
|
||||
|
||||
// Logout function
|
||||
const logout = async () => {
|
||||
try {
|
||||
await account.deleteSession('current')
|
||||
router.push('/') // Redirect to index.vue
|
||||
} catch (error) {
|
||||
console.error('Logout failed:', error.message)
|
||||
}
|
||||
}
|
||||
</script>
|
16
frontend/app/components/Sidebar.vue
Normal file
16
frontend/app/components/Sidebar.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<aside class="w-64 h-screen fixed bg-gray-900 text-white">
|
||||
<div class="p-4 text-xl font-bold border-b border-gray-700">
|
||||
🎓 StudentMS
|
||||
</div>
|
||||
<nav class="flex flex-col p-4 space-y-2">
|
||||
<NuxtLink to="/" class="p-2 rounded hover:bg-gray-800">Dashboard</NuxtLink>
|
||||
<NuxtLink to="/students" class="p-2 rounded hover:bg-gray-800">Students</NuxtLink>
|
||||
<NuxtLink to="/about" class="p-2 rounded hover:bg-gray-800">About</NuxtLink>
|
||||
</nav>
|
||||
<div class="p-4 border-t border-gray-700 text-sm mt-auto">
|
||||
© 2025 Atul Gunjal
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
11
frontend/nuxt.config.ts
Normal file
11
frontend/nuxt.config.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: "2024-11-01",
|
||||
devtools: { enabled: true },
|
||||
css: ['~/app/assets/style.css'],
|
||||
vite: {
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
],
|
||||
},
|
||||
});
|
12689
frontend/package-lock.json
generated
Normal file
12689
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
frontend/package.json
Normal file
22
frontend/package.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "nuxt-app",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxtjs/auth-next": "^5.0.0-1667386184.dfbbb54",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
"appwrite": "^17.0.2",
|
||||
"chart.js": "^4.4.9",
|
||||
"nuxt": "^3.16.2",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
}
|
||||
}
|
34
frontend/pages/Dashboard.vue
Normal file
34
frontend/pages/Dashboard.vue
Normal file
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-100 p-6">
|
||||
<h1 class="text-3xl font-bold text-gray-800 mb-6">Welcome to Your Dashboard</h1>
|
||||
|
||||
<div class="flex flex-wrap gap-6">
|
||||
<!-- Card 1 -->
|
||||
<div class="flex-1 min-w-[250px] bg-white p-6 rounded-2xl shadow hover:shadow-md transition">
|
||||
<h2 class="text-xl font-semibold text-gray-700 mb-2">Profile</h2>
|
||||
<p class="text-gray-500">View and edit your profile info.</p>
|
||||
</div>
|
||||
|
||||
<!-- Card 2 -->
|
||||
<div class="flex-1 min-w-[250px] bg-white p-6 rounded-2xl shadow hover:shadow-md transition">
|
||||
<h2 class="text-xl font-semibold text-gray-700 mb-2">Notifications</h2>
|
||||
<p class="text-gray-500">Check latest alerts and messages.</p>
|
||||
</div>
|
||||
|
||||
<!-- Card 3 -->
|
||||
<div class="flex-1 min-w-[250px] bg-white p-6 rounded-2xl shadow hover:shadow-md transition">
|
||||
<h2 class="text-xl font-semibold text-gray-700 mb-2">Settings</h2>
|
||||
<p class="text-gray-500">Manage account preferences.</p>
|
||||
</div>
|
||||
|
||||
<!-- Card 4 -->
|
||||
<div class="flex-1 min-w-[250px] bg-white p-6 rounded-2xl shadow hover:shadow-md transition">
|
||||
<h2 class="text-xl font-semibold text-gray-700 mb-2">Analytics</h2>
|
||||
<p class="text-gray-500">Track your app usage stats.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
118
frontend/pages/SignIn.vue
Normal file
118
frontend/pages/SignIn.vue
Normal file
@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex items-center justify-center bg-gray-100">
|
||||
<div class="w-full max-w-md bg-white p-8 rounded-2xl shadow-xl">
|
||||
<h1 class="text-2xl font-bold text-center mb-6">Login to Your Account</h1>
|
||||
|
||||
<form @submit.prevent="loginWithEmail" class="space-y-4">
|
||||
<input
|
||||
v-model="email"
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
required
|
||||
/>
|
||||
<input
|
||||
v-model="password"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="loading"
|
||||
class="w-full bg-blue-600 text-white py-2 rounded-md hover:bg-blue-700 transition disabled:opacity-50"
|
||||
>
|
||||
{{ loading ? 'Logging in...' : 'Login with Email' }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="mt-6 border-t pt-4">
|
||||
<p class="text-center text-gray-500 mb-4">or login with</p>
|
||||
|
||||
<div class="space-y-3">
|
||||
<button
|
||||
@click="loginWithOAuth('google')"
|
||||
class="w-full flex items-center justify-center bg-red-500 text-white py-2 rounded-md hover:bg-red-600 transition"
|
||||
>
|
||||
Google
|
||||
</button>
|
||||
<button
|
||||
@click="loginWithOAuth('github')"
|
||||
class="w-full flex items-center justify-center bg-gray-800 text-white py-2 rounded-md hover:bg-gray-900 transition"
|
||||
>
|
||||
GitHub
|
||||
</button>
|
||||
<button
|
||||
@click="loginWithOAuth('facebook')"
|
||||
class="w-full flex items-center justify-center bg-blue-700 text-white py-2 rounded-md hover:bg-blue-800 transition"
|
||||
>
|
||||
Facebook
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Client, Account } from 'appwrite'
|
||||
|
||||
const router = useRouter()
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
const loading = ref(false)
|
||||
|
||||
// ✅ Appwrite config
|
||||
const client = new Client()
|
||||
client
|
||||
.setEndpoint('https://cloud.appwrite.io/v1') // ✅ Fixed typo (removed double https)
|
||||
.setProject('67e1445400053dca1d9b') // Replace with your real project ID
|
||||
|
||||
const account = new Account(client)
|
||||
|
||||
// ✅ Login handler
|
||||
const loginWithEmail = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const session = await account.createEmailPasswordSession(email.value, password.value)
|
||||
console.log('Session:', session)
|
||||
alert('Login successful!')
|
||||
router.push('/Dashboard') // ✅ route to Dashboard
|
||||
} catch (error) {
|
||||
console.error('Login Error:', error.message)
|
||||
alert(error.message || 'Login failed. Please try again.')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------if user already logged in-------------
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const session = await account.getSession('current') // Check if session exists
|
||||
if (session) {
|
||||
router.push('/Dashboard') // Redirect to Dashboard if session exists
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('No active session found.')
|
||||
}
|
||||
})
|
||||
// ----------------------------------------------------------
|
||||
// ✅ OAuth login
|
||||
const loginWithOAuth = async (provider) => {
|
||||
try {
|
||||
account.createOAuth2Session(
|
||||
provider,
|
||||
'http://localhost:3000/Dashboard', // success redirect
|
||||
'http://localhost:3000/SignIn' // failure redirect
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(`OAuth login failed: ${error.message}`)
|
||||
alert('OAuth login failed.')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
93
frontend/pages/index.vue
Normal file
93
frontend/pages/index.vue
Normal file
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex items-center justify-center bg-gray-100">
|
||||
<div class="w-full max-w-md bg-white p-8 rounded-2xl shadow-xl">
|
||||
<h1 class="text-2xl font-bold text-center mb-6">Create Your Account</h1>
|
||||
|
||||
<form @submit.prevent="signUpWithEmail" class="space-y-4">
|
||||
<input
|
||||
v-model="name"
|
||||
type="text"
|
||||
placeholder="Name"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
required
|
||||
/>
|
||||
<input
|
||||
v-model="email"
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
required
|
||||
/>
|
||||
<input
|
||||
v-model="password"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full bg-green-600 text-white py-2 rounded-md hover:bg-green-700 transition"
|
||||
>
|
||||
Sign Up
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="mt-6 border-t pt-4">
|
||||
<p class="text-center text-gray-500 mb-4">or sign up with</p>
|
||||
|
||||
<div class="space-y-3">
|
||||
<button
|
||||
@click="signUpWithOAuth('github')"
|
||||
class="w-full flex items-center justify-center bg-gray-800 text-white py-2 rounded-md hover:bg-gray-900 transition"
|
||||
>
|
||||
GitHub
|
||||
</button>
|
||||
<button
|
||||
@click="signUpWithOAuth('facebook')"
|
||||
class="w-full flex items-center justify-center bg-blue-700 text-white py-2 rounded-md hover:bg-blue-800 transition"
|
||||
>
|
||||
Facebook
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-center mt-6 text-gray-500">
|
||||
Already have an account?
|
||||
<NuxtLink to="SignIn" class="text-blue-600 hover:underline">Login</NuxtLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { Client, Account, ID } from 'appwrite'
|
||||
|
||||
const client = new Client()
|
||||
client.setEndpoint('https://cloud.appwrite.io/v1').setProject('67e1445400053dca1d9b')
|
||||
|
||||
const account = new Account(client)
|
||||
|
||||
const name = ref('')
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
|
||||
const signUpWithEmail = async () => {
|
||||
try {
|
||||
await account.create(ID.unique(), email.value, password.value, name.value)
|
||||
alert('Account created successfully! Redirecting to login...')
|
||||
navigateTo('/SignIn')
|
||||
} catch (error) {
|
||||
alert('Signup failed: ' + error.message)
|
||||
}
|
||||
}
|
||||
|
||||
const signUpWithOAuth = (provider) => {
|
||||
account.createOAuth2Session(
|
||||
provider,
|
||||
'http://localhost:3000', // ✅ success redirect
|
||||
'http://localhost:3000/SignUp' // ❌ failure redirect (back to signup)
|
||||
)
|
||||
}
|
||||
</script>
|
BIN
frontend/public/favicon.ico
Normal file
BIN
frontend/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
2
frontend/public/robots.txt
Normal file
2
frontend/public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
||||
User-Agent: *
|
||||
Disallow:
|
3
frontend/server/tsconfig.json
Normal file
3
frontend/server/tsconfig.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../.nuxt/tsconfig.server.json"
|
||||
}
|
4
frontend/tsconfig.json
Normal file
4
frontend/tsconfig.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
// https://nuxt.com/docs/guide/concepts/typescript
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
Loading…
Reference in New Issue
Block a user