refactor: migrate from MongoDB to SQLite, updating database schema and configuration for improved performance and simplicity
This commit is contained in:
@@ -8,7 +8,7 @@ export const ERROR_MESSAGES: Record<string, string> = {
|
||||
|
||||
// MongoDB
|
||||
[ERROR_CODES.MONGODB.MISSING_URI]:
|
||||
"🔧 Please set MONGODB_URI environment variable in your .env file",
|
||||
"🔧 Please set DATABASE_URL environment variable in your .env file",
|
||||
[ERROR_CODES.MONGODB.CONNECTION_FAILED]: "🔌 MongoDB connection failed",
|
||||
|
||||
// Auth
|
||||
|
||||
@@ -3,12 +3,14 @@ import { getToken } from "next-auth/jwt";
|
||||
|
||||
export async function getAuthSession(request: NextRequest) {
|
||||
try {
|
||||
const cookieName = process.env.NODE_ENV === "production"
|
||||
? "__Secure-authjs.session-token"
|
||||
: "authjs.session-token";
|
||||
|
||||
const token = await getToken({
|
||||
req: request,
|
||||
secret: process.env.NEXTAUTH_SECRET,
|
||||
cookieName: process.env.NODE_ENV === "production"
|
||||
? "__Secure-authjs.session-token"
|
||||
: "authjs.session-token"
|
||||
cookieName
|
||||
});
|
||||
|
||||
if (!token) {
|
||||
|
||||
@@ -55,6 +55,8 @@ export class AdminService {
|
||||
|
||||
return {
|
||||
...user,
|
||||
id: user.id.toString(),
|
||||
roles: user.roles as string[],
|
||||
hasKomgaConfig: !!komgaConfig,
|
||||
hasPreferences: !!preferences,
|
||||
};
|
||||
@@ -76,10 +78,11 @@ export class AdminService {
|
||||
static async updateUserRoles(userId: string, roles: string[]): Promise<void> {
|
||||
try {
|
||||
await requireAdmin();
|
||||
const userIdInt = parseInt(userId, 10);
|
||||
|
||||
// Vérifier que l'utilisateur existe
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
where: { id: userIdInt },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@@ -88,7 +91,7 @@ export class AdminService {
|
||||
|
||||
// Mettre à jour les rôles
|
||||
await prisma.user.update({
|
||||
where: { id: userId },
|
||||
where: { id: userIdInt },
|
||||
data: { roles },
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -105,6 +108,7 @@ export class AdminService {
|
||||
static async deleteUser(userId: string): Promise<void> {
|
||||
try {
|
||||
const admin = await requireAdmin();
|
||||
const userIdInt = parseInt(userId, 10);
|
||||
|
||||
// Empêcher la suppression de son propre compte
|
||||
if (admin.id === userId) {
|
||||
@@ -113,7 +117,7 @@ export class AdminService {
|
||||
|
||||
// Vérifier que l'utilisateur existe
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
where: { id: userIdInt },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@@ -122,7 +126,7 @@ export class AdminService {
|
||||
|
||||
// Supprimer l'utilisateur (cascade supprimera les relations)
|
||||
await prisma.user.delete({
|
||||
where: { id: userId },
|
||||
where: { id: userIdInt },
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes("Forbidden")) {
|
||||
@@ -138,6 +142,7 @@ export class AdminService {
|
||||
static async resetUserPassword(userId: string, newPassword: string): Promise<void> {
|
||||
try {
|
||||
const admin = await requireAdmin();
|
||||
const userIdInt = parseInt(userId, 10);
|
||||
|
||||
// Empêcher la modification de son propre mot de passe via cette méthode
|
||||
if (admin.id === userId) {
|
||||
@@ -146,7 +151,7 @@ export class AdminService {
|
||||
|
||||
// Vérifier que l'utilisateur existe
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
where: { id: userIdInt },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@@ -159,7 +164,7 @@ export class AdminService {
|
||||
|
||||
// Mettre à jour le mot de passe
|
||||
await prisma.user.update({
|
||||
where: { id: userId },
|
||||
where: { id: userIdInt },
|
||||
data: { password: hashedPassword },
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -177,20 +182,21 @@ export class AdminService {
|
||||
try {
|
||||
await requireAdmin();
|
||||
|
||||
const [totalUsers, totalAdmins, usersWithKomga, usersWithPreferences] =
|
||||
const [totalUsers, usersWithKomga, usersWithPreferences] =
|
||||
await Promise.all([
|
||||
prisma.user.count(),
|
||||
prisma.user.count({
|
||||
where: {
|
||||
roles: {
|
||||
has: "ROLE_ADMIN",
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.komgaConfig.count(),
|
||||
prisma.preferences.count(),
|
||||
]);
|
||||
|
||||
// Count admin users by fetching all users and filtering
|
||||
const allUsers = await prisma.user.findMany({
|
||||
select: { roles: true },
|
||||
});
|
||||
const totalAdmins = allUsers.filter(user =>
|
||||
Array.isArray(user.roles) && user.roles.includes("ROLE_ADMIN")
|
||||
).length;
|
||||
|
||||
return {
|
||||
totalUsers,
|
||||
totalAdmins,
|
||||
|
||||
@@ -42,9 +42,9 @@ export class AuthServerService {
|
||||
});
|
||||
|
||||
const userData: UserData = {
|
||||
id: user.id,
|
||||
id: user.id.toString(),
|
||||
email: user.email,
|
||||
roles: user.roles,
|
||||
roles: user.roles as string[],
|
||||
authenticated: true,
|
||||
};
|
||||
|
||||
@@ -84,9 +84,9 @@ export class AuthServerService {
|
||||
}
|
||||
|
||||
const userData: UserData = {
|
||||
id: user.id,
|
||||
id: user.id.toString(),
|
||||
email: user.email,
|
||||
roles: user.roles,
|
||||
roles: user.roles as string[],
|
||||
authenticated: true,
|
||||
};
|
||||
|
||||
|
||||
@@ -16,20 +16,21 @@ export class ConfigDBService {
|
||||
static async saveConfig(data: KomgaConfigData): Promise<KomgaConfig> {
|
||||
try {
|
||||
const user: User | null = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
const authHeader: string = Buffer.from(`${data.username}:${data.password}`).toString(
|
||||
"base64"
|
||||
);
|
||||
|
||||
const config = await prisma.komgaConfig.upsert({
|
||||
where: { userId: user.id },
|
||||
where: { userId },
|
||||
update: {
|
||||
url: data.url,
|
||||
username: data.username,
|
||||
authHeader,
|
||||
},
|
||||
create: {
|
||||
userId: user.id,
|
||||
userId,
|
||||
url: data.url,
|
||||
username: data.username,
|
||||
authHeader,
|
||||
@@ -48,11 +49,12 @@ export class ConfigDBService {
|
||||
static async getConfig(): Promise<KomgaConfig | null> {
|
||||
try {
|
||||
const user: User | null = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
const config = await prisma.komgaConfig.findUnique({
|
||||
where: { userId: user.id },
|
||||
where: { userId },
|
||||
});
|
||||
return config as KomgaConfig | null;
|
||||
return config;
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
throw error;
|
||||
@@ -64,9 +66,10 @@ export class ConfigDBService {
|
||||
static async getTTLConfig(): Promise<TTLConfig | null> {
|
||||
try {
|
||||
const user: User | null = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
const config = await prisma.tTLConfig.findUnique({
|
||||
where: { userId: user.id },
|
||||
where: { userId },
|
||||
});
|
||||
return config as TTLConfig | null;
|
||||
} catch (error) {
|
||||
@@ -80,9 +83,10 @@ export class ConfigDBService {
|
||||
static async saveTTLConfig(data: TTLConfigData): Promise<TTLConfig> {
|
||||
try {
|
||||
const user: User | null = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
const config = await prisma.tTLConfig.upsert({
|
||||
where: { userId: user.id },
|
||||
where: { userId },
|
||||
update: {
|
||||
defaultTTL: data.defaultTTL,
|
||||
homeTTL: data.homeTTL,
|
||||
@@ -93,7 +97,7 @@ export class ConfigDBService {
|
||||
imageCacheMaxAge: data.imageCacheMaxAge,
|
||||
},
|
||||
create: {
|
||||
userId: user.id,
|
||||
userId,
|
||||
defaultTTL: data.defaultTTL,
|
||||
homeTTL: data.homeTTL,
|
||||
librariesTTL: data.librariesTTL,
|
||||
|
||||
@@ -28,10 +28,11 @@ export class FavoriteService {
|
||||
static async isFavorite(seriesId: string): Promise<boolean> {
|
||||
try {
|
||||
const user = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
const favorite = await prisma.favorite.findFirst({
|
||||
where: {
|
||||
userId: user.id,
|
||||
userId,
|
||||
seriesId: seriesId,
|
||||
},
|
||||
});
|
||||
@@ -48,17 +49,18 @@ export class FavoriteService {
|
||||
static async addToFavorites(seriesId: string): Promise<void> {
|
||||
try {
|
||||
const user = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
await prisma.favorite.upsert({
|
||||
where: {
|
||||
userId_seriesId: {
|
||||
userId: user.id,
|
||||
userId,
|
||||
seriesId,
|
||||
},
|
||||
},
|
||||
update: {},
|
||||
create: {
|
||||
userId: user.id,
|
||||
userId,
|
||||
seriesId,
|
||||
},
|
||||
});
|
||||
@@ -75,10 +77,11 @@ export class FavoriteService {
|
||||
static async removeFromFavorites(seriesId: string): Promise<void> {
|
||||
try {
|
||||
const user = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
await prisma.favorite.deleteMany({
|
||||
where: {
|
||||
userId: user.id,
|
||||
userId,
|
||||
seriesId,
|
||||
},
|
||||
});
|
||||
@@ -94,9 +97,10 @@ export class FavoriteService {
|
||||
*/
|
||||
static async getAllFavoriteIds(): Promise<string[]> {
|
||||
const user = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
const favorites = await prisma.favorite.findMany({
|
||||
where: { userId: user.id },
|
||||
where: { userId },
|
||||
select: { seriesId: true },
|
||||
});
|
||||
return favorites.map((favorite) => favorite.seriesId);
|
||||
@@ -104,17 +108,18 @@ export class FavoriteService {
|
||||
|
||||
static async addFavorite(seriesId: string) {
|
||||
const user = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
const favorite = await prisma.favorite.upsert({
|
||||
where: {
|
||||
userId_seriesId: {
|
||||
userId: user.id,
|
||||
userId,
|
||||
seriesId,
|
||||
},
|
||||
},
|
||||
update: {},
|
||||
create: {
|
||||
userId: user.id,
|
||||
userId,
|
||||
seriesId,
|
||||
},
|
||||
});
|
||||
@@ -123,10 +128,11 @@ export class FavoriteService {
|
||||
|
||||
static async removeFavorite(seriesId: string): Promise<boolean> {
|
||||
const user = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
const result = await prisma.favorite.deleteMany({
|
||||
where: {
|
||||
userId: user.id,
|
||||
userId,
|
||||
seriesId,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -19,8 +19,10 @@ export class PreferencesService {
|
||||
static async getPreferences(): Promise<UserPreferences> {
|
||||
try {
|
||||
const user = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
const preferences = await prisma.preferences.findUnique({
|
||||
where: { userId: user.id },
|
||||
where: { userId },
|
||||
});
|
||||
|
||||
if (!preferences) {
|
||||
@@ -45,6 +47,7 @@ export class PreferencesService {
|
||||
static async updatePreferences(preferences: Partial<UserPreferences>): Promise<UserPreferences> {
|
||||
try {
|
||||
const user = await this.getCurrentUser();
|
||||
const userId = parseInt(user.id, 10);
|
||||
|
||||
const updateData: Record<string, any> = {};
|
||||
if (preferences.showThumbnails !== undefined) updateData.showThumbnails = preferences.showThumbnails;
|
||||
@@ -54,15 +57,18 @@ export class PreferencesService {
|
||||
if (preferences.background !== undefined) updateData.background = preferences.background;
|
||||
|
||||
const updatedPreferences = await prisma.preferences.upsert({
|
||||
where: { userId: user.id },
|
||||
where: { userId },
|
||||
update: updateData,
|
||||
create: {
|
||||
userId: user.id,
|
||||
userId,
|
||||
showThumbnails: preferences.showThumbnails ?? defaultPreferences.showThumbnails,
|
||||
cacheMode: preferences.cacheMode ?? defaultPreferences.cacheMode,
|
||||
showOnlyUnread: preferences.showOnlyUnread ?? defaultPreferences.showOnlyUnread,
|
||||
displayMode: preferences.displayMode ?? defaultPreferences.displayMode,
|
||||
background: (preferences.background ?? defaultPreferences.background) as unknown as Prisma.InputJsonValue,
|
||||
circuitBreakerConfig: {},
|
||||
komgaMaxConcurrentRequests: 2,
|
||||
readerPrefetchCount: 5,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -21,9 +21,10 @@ export class UserService {
|
||||
if (!currentUser) {
|
||||
throw new AppError(ERROR_CODES.AUTH.UNAUTHENTICATED);
|
||||
}
|
||||
const userId = parseInt(currentUser.id, 10);
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: currentUser.id },
|
||||
where: { id: userId },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
@@ -37,7 +38,13 @@ export class UserService {
|
||||
throw new AppError(ERROR_CODES.AUTH.USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
return user;
|
||||
return {
|
||||
id: user.id.toString(),
|
||||
email: user.email,
|
||||
roles: user.roles as string[],
|
||||
createdAt: user.createdAt,
|
||||
updatedAt: user.updatedAt,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
throw error;
|
||||
@@ -55,10 +62,11 @@ export class UserService {
|
||||
if (!currentUser) {
|
||||
throw new AppError(ERROR_CODES.AUTH.UNAUTHENTICATED);
|
||||
}
|
||||
const userId = parseInt(currentUser.id, 10);
|
||||
|
||||
// Récupérer l'utilisateur avec son mot de passe
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: currentUser.id },
|
||||
where: { id: userId },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@@ -76,7 +84,7 @@ export class UserService {
|
||||
|
||||
// Mettre à jour le mot de passe
|
||||
await prisma.user.update({
|
||||
where: { id: currentUser.id },
|
||||
where: { id: userId },
|
||||
data: { password: hashedPassword },
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -93,16 +101,17 @@ export class UserService {
|
||||
if (!currentUser) {
|
||||
throw new AppError(ERROR_CODES.AUTH.UNAUTHENTICATED);
|
||||
}
|
||||
const userId = parseInt(currentUser.id, 10);
|
||||
|
||||
const [favoritesCount, preferences, komgaConfig] = await Promise.all([
|
||||
prisma.favorite.count({
|
||||
where: { userId: currentUser.id },
|
||||
where: { userId },
|
||||
}),
|
||||
prisma.preferences.findUnique({
|
||||
where: { userId: currentUser.id },
|
||||
where: { userId },
|
||||
}),
|
||||
prisma.komgaConfig.findUnique({
|
||||
where: { userId: currentUser.id },
|
||||
where: { userId },
|
||||
}),
|
||||
]);
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface KomgaConfigData {
|
||||
}
|
||||
|
||||
export interface KomgaConfig extends KomgaConfigData {
|
||||
userId: string;
|
||||
userId: number;
|
||||
}
|
||||
|
||||
export interface TTLConfigData {
|
||||
@@ -26,7 +26,7 @@ export interface TTLConfigData {
|
||||
}
|
||||
|
||||
export interface TTLConfig extends TTLConfigData {
|
||||
userId: string;
|
||||
userId: number;
|
||||
}
|
||||
|
||||
// Types liés à l'API Komga
|
||||
|
||||
Reference in New Issue
Block a user