feat: Init project with frontend and rudimentary authentication
This commit is contained in:
58
.gitignore
vendored
Normal file
58
.gitignore
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
# Nuxt dev/build outputs
|
||||
.output
|
||||
.data
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache
|
||||
dist
|
||||
|
||||
# Node dependencies
|
||||
node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.fleet
|
||||
sqlite.db
|
||||
|
||||
# Local env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
HELP.md
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Kotlin ###
|
||||
.kotlin
|
||||
1
legalconsenthub/.npmrc
Normal file
1
legalconsenthub/.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
shamefully-hoist=true
|
||||
1
legalconsenthub/.nvmrc
Normal file
1
legalconsenthub/.nvmrc
Normal file
@@ -0,0 +1 @@
|
||||
22
|
||||
23
legalconsenthub/README.md
Normal file
23
legalconsenthub/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Legal Consent Hub
|
||||
|
||||
## Setup
|
||||
|
||||
1. Create `.env` file with these variables:
|
||||
```
|
||||
BETTER_AUTH_URL=http://localhost:3000
|
||||
BETTER_AUTH_SECRET=YOUR_SECRET
|
||||
```
|
||||
2. Generate database schema: `npx @better-auth/cli generate`
|
||||
3. Migrate schema: `npx @better-auth/cli migrate`
|
||||
|
||||
## Common errors
|
||||
|
||||
### better-auth/cli generate
|
||||
```
|
||||
Couldn't read your auth config. Error: Could not locate the bindings file. Tried:
|
||||
```
|
||||
**Solution:** I was able to resolve by running npx node-gyp rebuild in 'node_modules/better-sqlite3'
|
||||
|
||||
https://github.com/WiseLibs/better-sqlite3/issues/1320
|
||||
|
||||
https://github.com/WiseLibs/better-sqlite3/issues/146
|
||||
6
legalconsenthub/app.vue
Normal file
6
legalconsenthub/app.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<NuxtLoadingIndicator />
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
@@ -0,0 +1,7 @@
|
||||
create table "better_auth_user" ("id" text not null primary key, "name" text not null, "email" text not null unique, "emailVerified" integer not null, "image" text, "createdAt" date not null, "updatedAt" date not null);
|
||||
|
||||
create table "better_auth_session" ("id" text not null primary key, "expiresAt" date not null, "token" text not null unique, "createdAt" date not null, "updatedAt" date not null, "ipAddress" text, "userAgent" text, "userId" text not null references "better_auth_user" ("id"));
|
||||
|
||||
create table "better_auth_account" ("id" text not null primary key, "accountId" text not null, "providerId" text not null, "userId" text not null references "better_auth_user" ("id"), "accessToken" text, "refreshToken" text, "idToken" text, "accessTokenExpiresAt" date, "refreshTokenExpiresAt" date, "scope" text, "password" text, "createdAt" date not null, "updatedAt" date not null);
|
||||
|
||||
create table "better_auth_verification" ("id" text not null primary key, "identifier" text not null, "value" text not null, "expiresAt" date not null, "createdAt" date, "updatedAt" date)
|
||||
77
legalconsenthub/components/Login.vue
Normal file
77
legalconsenthub/components/Login.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div class="form-container">
|
||||
<h2>Login</h2>
|
||||
<form @submit.prevent="handleLogin">
|
||||
<div class="form-group">
|
||||
<label for="email-login">Email:</label>
|
||||
<input type="email" id="email-login" v-model="email" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password-login">Passwort:</label>
|
||||
<input type="password" id="password-login" v-model="password" required />
|
||||
</div>
|
||||
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const email = ref('');
|
||||
const password = ref('');
|
||||
|
||||
const handleLogin = () => {
|
||||
if (!email.value || !password.value) {
|
||||
alert('Bitte alle Felder ausfüllen');
|
||||
return;
|
||||
}
|
||||
authClient.signIn.email({
|
||||
email: email.value,
|
||||
password: password.value
|
||||
}, {
|
||||
onRequest: (ctx) => {
|
||||
console.log("Sending login request", ctx)
|
||||
},
|
||||
onSuccess: (ctx) => {
|
||||
console.log("Successfully logged in!")
|
||||
},
|
||||
onError: (ctx) => {
|
||||
console.log(ctx.error.message);
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-container {
|
||||
max-width: 300px;
|
||||
margin: auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
background: #f9f9f9;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover {
|
||||
background: #0056b3;
|
||||
}
|
||||
</style>
|
||||
94
legalconsenthub/components/Register.vue
Normal file
94
legalconsenthub/components/Register.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div class="form-container">
|
||||
<h2>Registrieren</h2>
|
||||
<form @submit.prevent="handleRegister">
|
||||
<div class="form-group">
|
||||
<label for="username">Benutzername:</label>
|
||||
<input type="text" id="username" v-model="username" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Email:</label>
|
||||
<input type="email" id="email" v-model="email" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Passwort:</label>
|
||||
<input type="password" id="password" v-model="password" required />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="confirmPassword">Passwort bestätigen:</label>
|
||||
<input type="password" id="confirmPassword" v-model="confirmPassword" required />
|
||||
</div>
|
||||
|
||||
<button type="submit">Registrieren</button>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const username = ref('');
|
||||
const email = ref('');
|
||||
const password = ref('');
|
||||
const confirmPassword = ref('');
|
||||
|
||||
const handleRegister = () => {
|
||||
if (!username.value || !email.value || !password.value || !confirmPassword.value) {
|
||||
alert('Bitte alle Felder ausfüllen');
|
||||
return;
|
||||
}
|
||||
if (password.value !== confirmPassword.value) {
|
||||
alert('Passwörter stimmen nicht überein');
|
||||
return;
|
||||
}
|
||||
authClient.signUp.email({
|
||||
email: email.value,
|
||||
password: password.value,
|
||||
name: username.value
|
||||
}, {
|
||||
onRequest: (ctx) => {
|
||||
console.log("Sending register request", ctx)
|
||||
},
|
||||
onSuccess: (ctx) => {
|
||||
console.log("Successfully registered!")
|
||||
},
|
||||
onError: (ctx) => {
|
||||
console.log(ctx.error.message);
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-container {
|
||||
max-width: 300px;
|
||||
margin: auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
background: #f9f9f9;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover {
|
||||
background: #218838;
|
||||
}
|
||||
</style>
|
||||
6
legalconsenthub/layouts/default.vue
Normal file
6
legalconsenthub/layouts/default.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<slot/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
4
legalconsenthub/nuxt.config.ts
Normal file
4
legalconsenthub/nuxt.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default defineNuxtConfig({
|
||||
devtools: { enabled: true },
|
||||
compatibilityDate: '2024-11-01'
|
||||
})
|
||||
23
legalconsenthub/package.json
Normal file
23
legalconsenthub/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "legalconsenthub",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare",
|
||||
"fix:bettersqlite": "cd node_modules/better-sqlite3 && npx node-gyp rebuild && cd ../.."
|
||||
},
|
||||
"dependencies": {
|
||||
"better-auth": "^1.1.16",
|
||||
"better-sqlite3": "^11.8.1",
|
||||
"nuxt": "^3.15.4",
|
||||
"vue": "latest",
|
||||
"vue-router": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/better-sqlite3": "^7.6.12"
|
||||
}
|
||||
}
|
||||
16
legalconsenthub/pages/index.vue
Normal file
16
legalconsenthub/pages/index.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<pre>{{ session }}</pre>
|
||||
<h1>{{ session.data ? "Du bist eingeloggt" : "Nicht eingeloggt"}}</h1>
|
||||
<button v-if="session?.data" @click="authClient.signOut()">
|
||||
Sign out
|
||||
</button>
|
||||
</div>
|
||||
<Register />
|
||||
<Login />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
const session = authClient.useSession()
|
||||
</script>
|
||||
6708
legalconsenthub/pnpm-lock.yaml
generated
Normal file
6708
legalconsenthub/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
BIN
legalconsenthub/public/favicon.ico
Normal file
BIN
legalconsenthub/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
1
legalconsenthub/public/robots.txt
Normal file
1
legalconsenthub/public/robots.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
6
legalconsenthub/server/api/auth/[...].ts
Normal file
6
legalconsenthub/server/api/auth/[...].ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { auth } from "../../utils/auth";
|
||||
import {H3Event} from "h3";
|
||||
|
||||
export default defineEventHandler((event: H3Event) => {
|
||||
return auth.handler(toWebRequest(event));
|
||||
});
|
||||
3
legalconsenthub/server/tsconfig.json
Normal file
3
legalconsenthub/server/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../.nuxt/tsconfig.server.json"
|
||||
}
|
||||
11
legalconsenthub/server/utils/auth.ts
Normal file
11
legalconsenthub/server/utils/auth.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import Database from "better-sqlite3";
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: new Database("./sqlite.db"),
|
||||
emailAndPassword: { enabled: true, autoSignIn: false },
|
||||
user: { modelName: "better_auth_user" },
|
||||
session: { modelName: "better_auth_session" },
|
||||
account: { modelName: "better_auth_account" },
|
||||
verification: { modelName: "better_auth_verification" },
|
||||
});
|
||||
4
legalconsenthub/tsconfig.json
Normal file
4
legalconsenthub/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
// https://nuxt.com/docs/guide/concepts/typescript
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
||||
5
legalconsenthub/utils/auth-client.ts
Normal file
5
legalconsenthub/utils/auth-client.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { createAuthClient } from "better-auth/vue";
|
||||
|
||||
export const authClient = createAuthClient({
|
||||
baseURL: "http://localhost:3000",
|
||||
});
|
||||
Reference in New Issue
Block a user