feat(auth): add dummy auth foundation with global guard
This commit is contained in:
1 parent
9253cac1b2
commit
122dad35ce
12 files changed
+289
-87
No files matched your search
@@ -0,0 +1,82 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Pet" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"breed" TEXT NOT NULL,
|
||||
"birthDate" TIMESTAMP(3),
|
||||
"gender" TEXT,
|
||||
"bio" TEXT,
|
||||
"avatarUrl" TEXT,
|
||||
"visibility" TEXT NOT NULL DEFAULT 'public',
|
||||
"ownerId" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "Pet_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Post" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"content" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"petId" INTEGER NOT NULL,
|
||||
"authorId" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Follow" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"followerId" INTEGER NOT NULL,
|
||||
"followingId" INTEGER NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Follow_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Notification" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"payload" JSONB NOT NULL,
|
||||
"read" BOOLEAN NOT NULL DEFAULT false,
|
||||
"userId" INTEGER NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Notification_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Follow_followerId_followingId_key" ON "Follow"("followerId", "followingId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Pet" ADD CONSTRAINT "Pet_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Post" ADD CONSTRAINT "Post_petId_fkey" FOREIGN KEY ("petId") REFERENCES "Pet"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Follow" ADD CONSTRAINT "Follow_followerId_fkey" FOREIGN KEY ("followerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Follow" ADD CONSTRAINT "Follow_followingId_fkey" FOREIGN KEY ("followingId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Notification" ADD CONSTRAINT "Notification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
||||
+97
-87
@@ -3,125 +3,135 @@ import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
// -------- USERS --------
|
||||
console.log('DATABASE_URL:', process.env.DATABASE_URL);
|
||||
|
||||
/**
|
||||
* 1. USERS (ohne Passwort – Auth kommt später)
|
||||
*/
|
||||
const users = await Promise.all([
|
||||
prisma.user.create({
|
||||
data: { name: 'Alice', email: 'alice@test.local' },
|
||||
}),
|
||||
prisma.user.create({
|
||||
data: { name: 'Bob', email: 'bob@test.local' },
|
||||
}),
|
||||
prisma.user.create({
|
||||
data: { name: 'Clara', email: 'clara@test.local' },
|
||||
}),
|
||||
prisma.user.create({
|
||||
data: { name: 'David', email: 'david@test.local' },
|
||||
}),
|
||||
prisma.user.create({
|
||||
data: { name: 'Eva', email: 'eva@test.local' },
|
||||
}),
|
||||
]);
|
||||
prisma.user.upsert({
|
||||
where: { email: 'alice@onlypets.dev' },
|
||||
update: {},
|
||||
create: {
|
||||
email: 'alice@onlypets.dev',
|
||||
name: 'Alice',
|
||||
},
|
||||
}),
|
||||
prisma.user.upsert({
|
||||
where: { email: 'bob@onlypets.dev' },
|
||||
update: {},
|
||||
create: {
|
||||
email: 'bob@onlypets.dev',
|
||||
name: 'Bob',
|
||||
},
|
||||
}),
|
||||
prisma.user.upsert({
|
||||
where: { email: 'carla@onlypets.dev' },
|
||||
update: {},
|
||||
create: {
|
||||
email: 'carla@onlypets.dev',
|
||||
name: 'Carla',
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
// -------- PET HELPERS --------
|
||||
const yearsAgo = (y: number) =>
|
||||
new Date(new Date().getFullYear() - y, 5, 1);
|
||||
|
||||
const todayBirthday = new Date(
|
||||
new Date().getFullYear() - 3,
|
||||
new Date().getMonth(),
|
||||
new Date().getDate(),
|
||||
);
|
||||
|
||||
// -------- PETS --------
|
||||
/**
|
||||
* 2. PETS
|
||||
*/
|
||||
const pets = await Promise.all([
|
||||
prisma.pet.create({
|
||||
data: {
|
||||
name: 'Luna',
|
||||
type: 'Katze',
|
||||
breed: 'EKH',
|
||||
birthDate: todayBirthday, // 🎂 Geburtstag heute
|
||||
ownerId: users[0].id,
|
||||
visibility: 'public',
|
||||
},
|
||||
}),
|
||||
prisma.pet.create({
|
||||
data: {
|
||||
name: 'Max',
|
||||
type: 'Hund',
|
||||
breed: 'Labrador',
|
||||
birthDate: yearsAgo(5),
|
||||
ownerId: users[1].id,
|
||||
visibility: 'followers',
|
||||
},
|
||||
}),
|
||||
prisma.pet.create({
|
||||
data: {
|
||||
name: 'Milo',
|
||||
type: 'Katze',
|
||||
breed: 'BKH',
|
||||
birthDate: yearsAgo(2),
|
||||
ownerId: users[2].id,
|
||||
visibility: 'public',
|
||||
},
|
||||
}),
|
||||
prisma.pet.create({
|
||||
data: {
|
||||
name: 'Bella',
|
||||
type: 'Hund',
|
||||
breed: 'Mischling',
|
||||
birthDate: yearsAgo(7),
|
||||
ownerId: users[3].id,
|
||||
visibility: 'public',
|
||||
},
|
||||
}),
|
||||
]);
|
||||
prisma.pet.create({
|
||||
data: {
|
||||
name: 'Milo',
|
||||
type: 'Katze',
|
||||
breed: 'EKH',
|
||||
birthDate: new Date('2020-05-12'),
|
||||
ownerId: users[0].id,
|
||||
visibility: 'public',
|
||||
},
|
||||
}),
|
||||
prisma.pet.create({
|
||||
data: {
|
||||
name: 'Luna',
|
||||
type: 'Hund',
|
||||
breed: 'Labrador',
|
||||
birthDate: new Date('2019-11-03'),
|
||||
ownerId: users[1].id,
|
||||
visibility: 'followers',
|
||||
},
|
||||
}),
|
||||
prisma.pet.create({
|
||||
data: {
|
||||
name: 'Oscar',
|
||||
type: 'Katze',
|
||||
breed: 'BKH',
|
||||
birthDate: new Date('2022-02-01'),
|
||||
ownerId: users[2].id,
|
||||
visibility: 'public',
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
// -------- POSTS --------
|
||||
|
||||
/**
|
||||
* 3. POSTS
|
||||
*/
|
||||
await Promise.all([
|
||||
prisma.post.create({
|
||||
data: {
|
||||
content: 'Luna chillt in der Sonne ☀️',
|
||||
petId: pets[0].id,
|
||||
content: 'Milo liebt die Sonne ☀️',
|
||||
authorId: users[0].id,
|
||||
petId: pets[0].id,
|
||||
},
|
||||
}),
|
||||
prisma.post.create({
|
||||
data: {
|
||||
content: 'Milo hat heute Unsinn gemacht 😼',
|
||||
petId: pets[2].id,
|
||||
content: 'Luna beim Spaziergang 🐕',
|
||||
authorId: users[1].id,
|
||||
petId: pets[1].id,
|
||||
},
|
||||
}),
|
||||
prisma.post.create({
|
||||
data: {
|
||||
content: 'Oscar schläft schon wieder 😴',
|
||||
authorId: users[2].id,
|
||||
},
|
||||
}),
|
||||
prisma.post.create({
|
||||
data: {
|
||||
content: 'Bella nach dem Spaziergang 🐕',
|
||||
petId: pets[3].id,
|
||||
authorId: users[3].id,
|
||||
petId: pets[2].id,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
// -------- FOLLOWS --------
|
||||
/**
|
||||
* 4. FOLLOWS
|
||||
*/
|
||||
await Promise.all([
|
||||
prisma.follow.create({
|
||||
data: { followerId: users[1].id, followingId: users[0].id },
|
||||
data: {
|
||||
followerId: users[0].id,
|
||||
followingId: users[1].id,
|
||||
},
|
||||
}),
|
||||
prisma.follow.create({
|
||||
data: { followerId: users[2].id, followingId: users[0].id },
|
||||
data: {
|
||||
followerId: users[0].id,
|
||||
followingId: users[2].id,
|
||||
},
|
||||
}),
|
||||
prisma.follow.create({
|
||||
data: { followerId: users[3].id, followingId: users[1].id },
|
||||
data: {
|
||||
followerId: users[1].id,
|
||||
followingId: users[2].id,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
console.log('✅ Seed-Daten erfolgreich erstellt');
|
||||
console.log('✅ Seed erfolgreich ausgeführt');
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
console.error('❌ Seed fehlgeschlagen:', e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
|
||||
import { DummyAuthGuard } from './auth/guards/dummy-auth.guard';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: DummyAuthGuard,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,5 @@
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
|
||||
export const IS_PUBLIC_KEY = 'isPublic';
|
||||
|
||||
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
|
||||
@@ -0,0 +1,47 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { Request } from 'express';
|
||||
|
||||
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
|
||||
import { RequestUser, UserRole } from '../types/request-user.type';
|
||||
|
||||
@Injectable()
|
||||
export class DummyAuthGuard implements CanActivate {
|
||||
constructor(private readonly reflector: Reflector) {}
|
||||
|
||||
canActivate(context: ExecutionContext): boolean {
|
||||
const isPublic = this.reflector.getAllAndOverride<boolean>(
|
||||
IS_PUBLIC_KEY,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
|
||||
const request = context.switchToHttp().getRequest<Request>();
|
||||
|
||||
// Öffentliche Route → kein User nötig
|
||||
if (isPublic) {
|
||||
request.user = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Dummy-Auth nur aktiv, wenn explizit gesetzt
|
||||
if (process.env.AUTH_MODE !== 'dummy') {
|
||||
request.user = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
const role = (process.env.DEV_FAKE_USER_ROLE as UserRole) ?? 'USER';
|
||||
|
||||
const fakeUser: RequestUser = {
|
||||
id: Number(process.env.DEV_FAKE_USER_ID ?? 0),
|
||||
role,
|
||||
isFake: process.env.DEV_FAKE_USER === 'true',
|
||||
};
|
||||
|
||||
request.user = fakeUser;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
export type UserRole = 'USER' | 'ADMIN' | 'MOD';
|
||||
|
||||
export interface RequestUser {
|
||||
/** User-ID aus DB oder Fake-User */
|
||||
id: number;
|
||||
|
||||
/** Rollenbasis für Guards & Admin-Features */
|
||||
role: UserRole;
|
||||
|
||||
/** Für DEV / Dummy-Auth */
|
||||
isFake?: boolean;
|
||||
|
||||
/** Optional, future-proof (z. B. Feed-Optimierung) */
|
||||
petIds?: number[];
|
||||
}
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
import { RequestUser } from '../auth/types/request-user.type';
|
||||
|
||||
declare module 'express' {
|
||||
interface Request {
|
||||
user?: RequestUser | null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
container_name: onlypets-postgres
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "55432:5432"
|
||||
environment:
|
||||
POSTGRES_USER: onlypets_user
|
||||
POSTGRES_PASSWORD: onlypets_pass
|
||||
POSTGRES_DB: onlypets
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
Reference in new issue
Block a user