Skip to content

Commit 7a2ed36

Browse files
committed
implement user database
1 parent 9eba6ed commit 7a2ed36

File tree

15 files changed

+445
-33
lines changed

15 files changed

+445
-33
lines changed

server/.env.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
PORT=2000
22
DEBUG=*.*
3-
DATABASE_URL="file:../db/kubero.db"
3+
DATABASE_URL="file:../db/kubero.sqlite?mode=rwc&_journal=WAL&_busy_timeout=5000"
44
LOGLEVEL=verbose
55
NODE_ENV=production
66

server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
"test:cov": "jest --coverage",
2222
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
2323
"test:e2e": "jest --config ./test/jest-e2e.json",
24+
"prisma:init": "npx prisma migrate dev --name init --create-only",
2425
"prisma:generate": "npx prisma generate",
2526
"prisma:studio": "npx prisma studio",
2627
"prisma:reset": "npx prisma migrate reset --force",
27-
"prisma:init": "npx prisma migrate dev --name init --create-only",
2828
"prisma:deploy": "npx prisma migrate deploy",
2929
"prisma:migrate": "npx prisma migrate deploy",
3030
"prisma:push": "npx prisma db push",

server/prisma/migrations/20250618115501_init/migration.sql renamed to server/prisma/migrations/20250620120259_init/migration.sql

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
CREATE TABLE "Audit" (
33
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
44
"timestamp" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
5-
"user" TEXT NOT NULL,
65
"severity" TEXT NOT NULL DEFAULT 'normal',
76
"action" TEXT NOT NULL,
87
"namespace" TEXT NOT NULL,
@@ -11,14 +10,20 @@ CREATE TABLE "Audit" (
1110
"pipeline" TEXT NOT NULL,
1211
"resource" TEXT NOT NULL DEFAULT 'unknown',
1312
"message" TEXT NOT NULL,
13+
"user" TEXT NOT NULL,
1414
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
15-
"updatedAt" DATETIME NOT NULL
15+
"updatedAt" DATETIME NOT NULL,
16+
CONSTRAINT "Audit_user_fkey" FOREIGN KEY ("user") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
1617
);
1718

1819
-- CreateTable
1920
CREATE TABLE "User" (
2021
"id" TEXT NOT NULL PRIMARY KEY,
21-
"name" TEXT,
22+
"username" TEXT NOT NULL,
23+
"firstName" TEXT,
24+
"lastName" TEXT,
25+
"company" TEXT,
26+
"location" TEXT,
2227
"email" TEXT NOT NULL,
2328
"emailVerified" DATETIME,
2429
"password" TEXT NOT NULL,
@@ -104,6 +109,9 @@ CREATE TABLE "_PermissionToToken" (
104109
CONSTRAINT "_PermissionToToken_B_fkey" FOREIGN KEY ("B") REFERENCES "Token" ("id") ON DELETE CASCADE ON UPDATE CASCADE
105110
);
106111

112+
-- CreateIndex
113+
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
114+
107115
-- CreateIndex
108116
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
109117

server/prisma/schema.prisma

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ generator client {
2121
model Audit {
2222
id Int @id @default(autoincrement())
2323
timestamp DateTime @default(now())
24-
user String
2524
severity Severity @default(normal)
2625
action String
2726
namespace String
@@ -31,6 +30,9 @@ model Audit {
3130
resource ResourceType @default(unknown)
3231
message String
3332
33+
user String
34+
users User @relation(fields: [user], references: [id])
35+
3436
createdAt DateTime @default(now())
3537
updatedAt DateTime @updatedAt
3638
}
@@ -65,7 +67,11 @@ enum ResourceType {
6567

6668
model User {
6769
id String @id @default(cuid())
68-
name String?
70+
username String @unique
71+
firstName String?
72+
lastName String?
73+
company String?
74+
location String?
6975
email String @unique
7076
emailVerified DateTime?
7177
password String
@@ -89,6 +95,7 @@ model User {
8995
9096
createdAt DateTime @default(now())
9197
updatedAt DateTime @updatedAt
98+
Audit Audit[]
9299
}
93100

94101
model UserGroup {
@@ -98,16 +105,16 @@ model UserGroup {
98105
99106
users User[]
100107
101-
createdAt DateTime @default(now())
102-
updatedAt DateTime @updatedAt
108+
createdAt DateTime @default(now())
109+
updatedAt DateTime @updatedAt
103110
}
104111

105112
model Role {
106113
id String @id @default(cuid())
107114
name String @unique
108115
description String?
109116
110-
users User[] // Users associated with this role
117+
users User[] // Users associated with this role
111118
permissions Permission[] // Permissions directly assigned to this role
112119
113120
createdAt DateTime @default(now())
@@ -127,8 +134,8 @@ model Token {
127134
128135
permissions Permission[] // Permissions associated with this token
129136
130-
createdAt DateTime @default(now())
131-
updatedAt DateTime @updatedAt
137+
createdAt DateTime @default(now())
138+
updatedAt DateTime @updatedAt
132139
}
133140

134141
model Permission {
@@ -137,9 +144,9 @@ model Permission {
137144
action String // e.g., "create", "read", "update", "delete"
138145
namespace String? // Optional namespace for scoping permissions
139146
140-
roles Role[] // Roles that have this permission
141-
tokens Token[] // Tokens that have this permission
147+
roles Role[] // Roles that have this permission
148+
tokens Token[] // Tokens that have this permission
142149
143-
createdAt DateTime @default(now())
144-
updatedAt DateTime @updatedAt
150+
createdAt DateTime @default(now())
151+
updatedAt DateTime @updatedAt
145152
}

server/src/audit/audit.service.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class AuditService {
2929
return;
3030
}
3131
const auditEntry: AuditEntry = {
32-
user: 'kubero',
32+
user: '1',
3333
severity: 'normal',
3434
action: 'start',
3535
namespace: '',
@@ -54,6 +54,12 @@ export class AuditService {
5454
return;
5555
}
5656
try {
57+
if (entry.user === '' || entry.user === null) {
58+
this.logger.debug(
59+
'Audit log entry without user. Defaulting to system user.',
60+
);
61+
entry.user = '1'; // Default to system user if not provided
62+
}
5763
await this.prisma.audit.create({
5864
data: {
5965
user: entry.user,
@@ -65,7 +71,6 @@ export class AuditService {
6571
pipeline: entry.pipeline,
6672
resource: entry.resource,
6773
message: entry.message,
68-
// timestamp wird automatisch gesetzt, falls im Prisma-Schema so definiert
6974
},
7075
});
7176
await this.limit(this.logmaxbackups);

server/src/database/database.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Global, Module, Logger } from '@nestjs/common';
22
import { DatabaseService } from './database.service';
33
import { PrismaClient } from '@prisma/client';
44

5-
DatabaseService.Init(); // configing the database connection initialization
5+
//DatabaseService.Init(); // configing the database connection initialization
66

77
@Global()
88
@Module({
Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,34 @@
11
import { Injectable, Logger } from '@nestjs/common';
2-
import { PrismaClient } from '@prisma/client';
2+
import { PrismaClient } from '@prisma/client';
3+
import * as crypto from 'crypto';
4+
import * as bcrypt from 'bcrypt';
35

46
@Injectable()
57
export class DatabaseService {
68

7-
public static async Init() {
9+
private static readonly logger = new Logger(DatabaseService.name);
10+
private static readonly prisma = new PrismaClient();
11+
12+
constructor() {
13+
// Initialize the Prisma client
14+
DatabaseService.prisma.$connect()
15+
.then(() => {
16+
DatabaseService.logger.log('Connected to the database successfully.');
17+
})
18+
.catch((error) => {
19+
DatabaseService.logger.error('Failed to connect to the database.', error);
20+
});
21+
this.runMigrations()
22+
.then(() => {
23+
// create user after migrations
24+
this.createAdminUser()
25+
})
26+
.catch((error) => {
27+
DatabaseService.logger.error('Error during database migrations.', error);
28+
});
29+
}
30+
31+
private async init() {
832
if (process.env.DATABASE_URL === '' || process.env.DATABASE_URL === undefined) {
933
process.env.DATABASE_URL = 'file:../db/kubero.sqlite';
1034
process.env.DATABASE_TYPE = 'sqlite';
@@ -15,24 +39,89 @@ export class DatabaseService {
1539
}
1640
}
1741

18-
public static async RunMigrations() {
42+
private async runMigrations() {
43+
const { execSync } = await import('child_process');
1944

20-
await this.Init();
45+
await this.init();
2146

2247
const prisma = new PrismaClient();
2348

2449
try {
2550
Logger.log('Running Prisma migrations...', 'Bootstrap');
2651
// @ts-ignore
2752
await prisma.$executeRawUnsafe?.('PRAGMA foreign_keys=OFF;'); // For SQLite, optional
28-
await prisma.$disconnect();
2953
// Use CLI for migrations
30-
const { execSync } = await import('child_process');
3154
execSync('npx prisma migrate deploy', { stdio: 'inherit' });
55+
//execSync('npx prisma migrate deploy', {});
3256
Logger.log('Prisma migrations completed.', 'Bootstrap');
57+
await prisma.$executeRaw`
58+
INSERT INTO "User" (
59+
"id",
60+
"email",
61+
"username",
62+
"password",
63+
"isActive",
64+
createdAt,
65+
updatedAt
66+
) VALUES (
67+
"1",
68+
69+
'system',
70+
'',
71+
false,
72+
CURRENT_TIMESTAMP,
73+
CURRENT_TIMESTAMP
74+
) ON CONFLICT DO NOTHING;`
75+
await prisma.$disconnect();
3376
} catch (err) {
3477
Logger.error('Prisma migration failed', err, 'Bootstrap');
3578
process.exit(1);
3679
}
3780
}
81+
82+
private async createAdminUser() {
83+
const prisma = new PrismaClient();
84+
85+
// Check if the admin user already exists
86+
const existingUser = await prisma.user.findUnique({
87+
where: { id: '2' },
88+
});
89+
if (existingUser) {
90+
Logger.log('Admin user already exists. Skipping creation.', 'DatabaseService');
91+
return;
92+
}
93+
94+
const adminUser = process.env.KUBERO_ADMIN_USERNAME || 'admin';
95+
const adminEmail = process.env.KUBERO_ADMIN_EMAIL || '[email protected]';
96+
97+
try {
98+
99+
// Generiere ein zufälliges Passwort
100+
const plainPassword = crypto.randomBytes(25).toString('base64').slice(0, 19);
101+
// Erstelle einen bcrypt-Hash
102+
const passwordHash = await bcrypt.hash(plainPassword, 10);
103+
console.log('\n\n\n', 'Admin account created since no user exists yet');
104+
console.log('Please change the password after the first login.');
105+
console.log('Admin credentials:');
106+
console.log(' username: ', adminUser);
107+
console.log(' password: ', plainPassword);
108+
console.log(' email: ', adminEmail, '\n\n\n');
109+
110+
await prisma.user.create({
111+
data: {
112+
id: '2',
113+
username: adminUser,
114+
email: adminEmail,
115+
password: passwordHash,
116+
isActive: true,
117+
createdAt: new Date(),
118+
updatedAt: new Date(),
119+
},
120+
});
121+
Logger.log('Admin user created successfully.', 'DatabaseService');
122+
} catch (error) {
123+
Logger.error('Failed to create admin user.', error, 'DatabaseService');
124+
}
125+
await prisma.$disconnect();
126+
}
38127
}

server/src/main.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ async function bootstrap() {
3131
cors: true,
3232
});
3333

34-
DatabaseService.RunMigrations();
34+
//DatabaseService.RunMigrations();
35+
//DatabaseService.CreateAdminUser();
3536

3637
app.use(
3738
helmet({

server/src/users/dto/users.dto.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
3+
export class UserDTO {
4+
@ApiProperty()
5+
id: string;
6+
7+
@ApiProperty({ required: true })
8+
username: string;
9+
10+
@ApiProperty({ required: false })
11+
firstName?: string;
12+
13+
@ApiProperty({ required: false })
14+
lastName?: string;
15+
16+
@ApiProperty()
17+
email: string;
18+
19+
@ApiProperty({ required: false })
20+
emailVerified?: Date;
21+
22+
@ApiProperty({ required: false })
23+
image?: string;
24+
25+
@ApiProperty({ required: false })
26+
isActive?: boolean;
27+
}
28+
29+
export class GetAllUsersDTO {
30+
@ApiProperty({ type: [UserDTO] })
31+
users: UserDTO[];
32+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { UsersController } from './users.controller';
3+
4+
describe('UsersController', () => {
5+
let controller: UsersController;
6+
7+
beforeEach(async () => {
8+
const module: TestingModule = await Test.createTestingModule({
9+
controllers: [UsersController],
10+
}).compile();
11+
12+
controller = module.get<UsersController>(UsersController);
13+
});
14+
15+
it('should be defined', () => {
16+
expect(controller).toBeDefined();
17+
});
18+
});

0 commit comments

Comments
 (0)