mirror of
https://github.com/jeffvli/sonixd.git
synced 2026-04-30 11:12:36 -04:00
Rename files, add utils
This commit is contained in:
@@ -17,7 +17,7 @@ const getAlbumArtists = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
const { limit, page, serverFolderIds } = req.query;
|
||||
const data = await albumArtistsService.getMany(req, {
|
||||
const data = await albumArtistsService.findMany(req, {
|
||||
limit: Number(limit),
|
||||
page: Number(page),
|
||||
serverFolderIds: String(serverFolderIds),
|
||||
@@ -27,15 +27,15 @@ const getAlbumArtists = async (req: Request, res: Response) => {
|
||||
return res.status(data.statusCode).json(getSuccessResponse(data));
|
||||
};
|
||||
|
||||
const getAlbumArtist = async (req: Request, res: Response) => {
|
||||
const getAlbumArtistById = async (req: Request, res: Response) => {
|
||||
validateRequest(req, { params: z.object({ ...idValidation }) });
|
||||
|
||||
const { id } = req.params;
|
||||
const data = await albumArtistsService.getOne({
|
||||
const data = await albumArtistsService.findById({
|
||||
id: Number(id),
|
||||
user: req.auth,
|
||||
});
|
||||
return res.status(data.statusCode).json(getSuccessResponse(data));
|
||||
};
|
||||
|
||||
export const albumArtistsController = { getAlbumArtist, getAlbumArtists };
|
||||
export const albumArtistsController = { getAlbumArtistById, getAlbumArtists };
|
||||
@@ -8,14 +8,20 @@ import {
|
||||
validateRequest,
|
||||
} from '../utils';
|
||||
|
||||
const getAlbum = async (req: Request, res: Response) => {
|
||||
validateRequest(req, { params: z.object({ ...idValidation }) });
|
||||
const getAlbumById = async (req: Request, res: Response) => {
|
||||
validateRequest(req, {
|
||||
params: z.object({ ...idValidation }),
|
||||
query: z.object({ serverUrls: z.optional(z.string().min(1)) }),
|
||||
});
|
||||
|
||||
const { id } = req.params;
|
||||
const data = await albumsService.getOne({
|
||||
const { serverUrls } = req.query;
|
||||
const data = await albumsService.findById({
|
||||
id: Number(id),
|
||||
serverUrls: serverUrls && String(serverUrls),
|
||||
user: req.auth,
|
||||
});
|
||||
|
||||
return res.status(data.statusCode).json(getSuccessResponse(data));
|
||||
};
|
||||
|
||||
@@ -23,15 +29,17 @@ const getAlbums = async (req: Request, res: Response) => {
|
||||
validateRequest(req, {
|
||||
query: z.object({
|
||||
...paginationValidation,
|
||||
serverFolderIds: z.string().min(1),
|
||||
serverFolderIds: z.optional(z.string().min(1)),
|
||||
serverUrls: z.optional(z.string().min(1)),
|
||||
}),
|
||||
});
|
||||
|
||||
const { limit, page, serverFolderIds } = req.query;
|
||||
const data = await albumsService.getMany(req, {
|
||||
const { limit, page, serverFolderIds, serverUrls } = req.query;
|
||||
const data = await albumsService.findMany(req, {
|
||||
limit: Number(limit),
|
||||
page: Number(page),
|
||||
serverFolderIds: String(serverFolderIds),
|
||||
serverFolderIds: serverFolderIds && String(serverFolderIds),
|
||||
serverUrls: serverUrls && String(serverUrls),
|
||||
user: req.auth,
|
||||
});
|
||||
|
||||
@@ -39,6 +47,6 @@ const getAlbums = async (req: Request, res: Response) => {
|
||||
};
|
||||
|
||||
export const albumsController = {
|
||||
getAlbum,
|
||||
getAlbumById,
|
||||
getAlbums,
|
||||
};
|
||||
@@ -8,11 +8,11 @@ import {
|
||||
validateRequest,
|
||||
} from '../utils';
|
||||
|
||||
const getArtist = async (req: Request, res: Response) => {
|
||||
const getArtistById = async (req: Request, res: Response) => {
|
||||
validateRequest(req, { params: z.object({ ...idValidation }) });
|
||||
|
||||
const { id } = req.params;
|
||||
const data = await artistsService.getOne({
|
||||
const data = await artistsService.findById({
|
||||
id: Number(id),
|
||||
user: req.auth,
|
||||
});
|
||||
@@ -28,7 +28,7 @@ const getArtists = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
const { limit, page, serverFolderIds } = req.query;
|
||||
const data = await artistsService.getMany(req, {
|
||||
const data = await artistsService.findMany(req, {
|
||||
limit: Number(limit),
|
||||
page: Number(page),
|
||||
serverFolderIds: String(serverFolderIds),
|
||||
@@ -38,4 +38,4 @@ const getArtists = async (req: Request, res: Response) => {
|
||||
return res.status(data.statusCode).json(getSuccessResponse(data));
|
||||
};
|
||||
|
||||
export const artistsController = { getArtist, getArtists };
|
||||
export const artistsController = { getArtistById, getArtists };
|
||||
@@ -1,6 +1,6 @@
|
||||
export * from './album-artists-controller';
|
||||
export * from './auth-controller';
|
||||
export * from './servers-controller';
|
||||
export * from './users-controller';
|
||||
export * from './artists-controller';
|
||||
export * from './albums-controller';
|
||||
export * from './album-artists.controller';
|
||||
export * from './auth.controller';
|
||||
export * from './servers.controller';
|
||||
export * from './users.controller';
|
||||
export * from './artists.controller';
|
||||
export * from './albums.controller';
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { z } from 'zod';
|
||||
import { prisma } from '../lib';
|
||||
import { serversService } from '../services';
|
||||
import { getSuccessResponse, idValidation, validateRequest } from '../utils';
|
||||
|
||||
const getServer = async (req: Request, res: Response) => {
|
||||
const getServerById = async (req: Request, res: Response) => {
|
||||
validateRequest(req, { params: z.object({ ...idValidation }) });
|
||||
|
||||
const { id } = req.params;
|
||||
const data = await serversService.getOne(req.auth, {
|
||||
const data = await serversService.findById(req.auth, {
|
||||
id: Number(id),
|
||||
});
|
||||
|
||||
@@ -15,22 +16,13 @@ const getServer = async (req: Request, res: Response) => {
|
||||
};
|
||||
|
||||
const getServers = async (req: Request, res: Response) => {
|
||||
const data = await serversService.getMany(req.auth);
|
||||
const data = await serversService.findMany(req.auth);
|
||||
|
||||
return res.status(data.statusCode).json(getSuccessResponse(data));
|
||||
};
|
||||
|
||||
const createServer = async (req: Request, res: Response) => {
|
||||
const { name, url, username, remoteUserId, token, serverType } = req.body;
|
||||
|
||||
const data = await serversService.create({
|
||||
name,
|
||||
remoteUserId,
|
||||
serverType,
|
||||
token,
|
||||
url,
|
||||
username,
|
||||
});
|
||||
const data = await serversService.create(req.body);
|
||||
|
||||
return res.status(data.statusCode).json(getSuccessResponse(data));
|
||||
};
|
||||
@@ -59,9 +51,21 @@ const scanServer = async (req: Request, res: Response) => {
|
||||
return res.status(data.statusCode).json(getSuccessResponse(data));
|
||||
};
|
||||
|
||||
const getFolder = async (req: Request, res: Response) => {
|
||||
const data = await prisma.folder.findUnique({
|
||||
include: {
|
||||
children: true,
|
||||
},
|
||||
where: { id: Number(req.params.id) },
|
||||
});
|
||||
|
||||
return res.status(200).json(getSuccessResponse({ data, statusCode: 200 }));
|
||||
};
|
||||
|
||||
export const serversController = {
|
||||
createServer,
|
||||
getServer,
|
||||
getFolder,
|
||||
getServerById,
|
||||
getServers,
|
||||
refreshServer,
|
||||
scanServer,
|
||||
35
src/server/controllers/songs.controller.ts
Normal file
35
src/server/controllers/songs.controller.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { z } from 'zod';
|
||||
import { songsService } from '../services/songs.service';
|
||||
import {
|
||||
getSuccessResponse,
|
||||
paginationValidation,
|
||||
validateRequest,
|
||||
} from '../utils';
|
||||
|
||||
const getSongs = async (req: Request, res: Response) => {
|
||||
validateRequest(req, {
|
||||
query: z.object({
|
||||
...paginationValidation,
|
||||
albumIds: z.optional(z.string()),
|
||||
artistIds: z.optional(z.string()),
|
||||
serverFolderIds: z.optional(z.string().min(1)),
|
||||
songIds: z.optional(z.string()),
|
||||
}),
|
||||
});
|
||||
|
||||
const { limit, page, serverFolderIds } = req.query;
|
||||
|
||||
const data = await songsService.findMany(req, {
|
||||
limit: Number(limit),
|
||||
page: Number(page),
|
||||
serverFolderIds: String(serverFolderIds),
|
||||
user: req.auth,
|
||||
});
|
||||
|
||||
return res.status(data.statusCode).json(getSuccessResponse(data));
|
||||
};
|
||||
|
||||
export const songsController = {
|
||||
getSongs,
|
||||
};
|
||||
@@ -13,5 +13,5 @@ albumArtistsRouter.get(
|
||||
albumArtistsRouter.get(
|
||||
'/:id',
|
||||
authenticateLocal,
|
||||
albumArtistsController.getAlbumArtist
|
||||
albumArtistsController.getAlbumArtistById
|
||||
);
|
||||
@@ -6,4 +6,4 @@ export const albumsRouter: Router = express.Router();
|
||||
|
||||
albumsRouter.get('/', authenticateLocal, albumsController.getAlbums);
|
||||
|
||||
albumsRouter.get('/:id', authenticateLocal, albumsController.getAlbum);
|
||||
albumsRouter.get('/:id', authenticateLocal, albumsController.getAlbumById);
|
||||
@@ -6,4 +6,4 @@ export const artistsRouter: Router = express.Router();
|
||||
|
||||
artistsRouter.get('/', authenticateLocal, artistsController.getArtists);
|
||||
|
||||
artistsRouter.get('/:id', authenticateLocal, artistsController.getArtist);
|
||||
artistsRouter.get('/:id', authenticateLocal, artistsController.getArtistById);
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Router } from 'express';
|
||||
import { albumArtistsRouter } from './album-artists-route';
|
||||
import { albumsRouter } from './albums-route';
|
||||
import { artistsRouter } from './artists-route';
|
||||
import { authRouter } from './auth-route';
|
||||
import { serversRouter } from './servers-route';
|
||||
import { tasksRouter } from './tasks-route';
|
||||
import { usersRouter } from './users-route';
|
||||
import { albumArtistsRouter } from './album-artists.route';
|
||||
import { albumsRouter } from './albums.route';
|
||||
import { artistsRouter } from './artists.route';
|
||||
import { authRouter } from './auth.route';
|
||||
import { serversRouter } from './servers.route';
|
||||
import { songsRouter } from './songs.route';
|
||||
import { tasksRouter } from './tasks.route';
|
||||
import { usersRouter } from './users.route';
|
||||
|
||||
export const routes = Router();
|
||||
|
||||
@@ -16,3 +17,4 @@ routes.use('/api/users', usersRouter);
|
||||
routes.use('/api/album-artists', albumArtistsRouter);
|
||||
routes.use('/api/artists', artistsRouter);
|
||||
routes.use('/api/albums', albumsRouter);
|
||||
routes.use('/api/songs', songsRouter);
|
||||
|
||||
@@ -6,7 +6,7 @@ export const serversRouter: Router = express.Router();
|
||||
|
||||
serversRouter.get('/', authenticateLocal, serversController.getServers);
|
||||
|
||||
serversRouter.get('/:id', authenticateLocal, serversController.getServer);
|
||||
serversRouter.get('/:id', authenticateLocal, serversController.getServerById);
|
||||
|
||||
serversRouter.get(
|
||||
'/:id/refresh',
|
||||
@@ -14,6 +14,12 @@ serversRouter.get(
|
||||
serversController.refreshServer
|
||||
);
|
||||
|
||||
serversRouter.get(
|
||||
'/:id/folder',
|
||||
authenticateAdmin,
|
||||
serversController.getFolder
|
||||
);
|
||||
|
||||
serversRouter.post('/', authenticateAdmin, serversController.createServer);
|
||||
|
||||
serversRouter.post(
|
||||
7
src/server/routes/songs.route.ts
Normal file
7
src/server/routes/songs.route.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import express, { Router } from 'express';
|
||||
import { songsController } from '../controllers/songs.controller';
|
||||
import { authenticateLocal } from '../middleware';
|
||||
|
||||
export const songsRouter: Router = express.Router();
|
||||
|
||||
songsRouter.get('/', authenticateLocal, songsController.getSongs);
|
||||
@@ -4,46 +4,51 @@ import { OffsetPagination, User } from '../types/types';
|
||||
import {
|
||||
ApiError,
|
||||
ApiSuccess,
|
||||
hasFolderAccess,
|
||||
folderPermissions,
|
||||
splitNumberString,
|
||||
} from '../utils';
|
||||
|
||||
const getOne = async (options: { id: number; user: User }) => {
|
||||
const findById = async (options: { id: number; user: User }) => {
|
||||
const { id, user } = options;
|
||||
const album = await prisma.albumArtist.findUnique({
|
||||
const albumArtist = await prisma.albumArtist.findUnique({
|
||||
include: {
|
||||
albums: { include: { songs: true } },
|
||||
genres: true,
|
||||
images: true,
|
||||
serverFolders: true,
|
||||
},
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!album) {
|
||||
if (!albumArtist) {
|
||||
throw ApiError.notFound('');
|
||||
}
|
||||
|
||||
if (!(await hasFolderAccess([album?.serverFolderId], user))) {
|
||||
const serverFolderIds = albumArtist.serverFolders.map(
|
||||
(serverFolder) => serverFolder.id
|
||||
);
|
||||
|
||||
if (!(await folderPermissions(serverFolderIds, user))) {
|
||||
throw ApiError.forbidden('');
|
||||
}
|
||||
|
||||
return ApiSuccess.ok({ data: album });
|
||||
return ApiSuccess.ok({ data: albumArtist });
|
||||
};
|
||||
|
||||
const getMany = async (
|
||||
const findMany = async (
|
||||
req: Request,
|
||||
options: { serverFolderIds: string; user: User } & OffsetPagination
|
||||
) => {
|
||||
const { user, limit, page, serverFolderIds: rServerFolderIds } = options;
|
||||
const serverFolderIds = splitNumberString(rServerFolderIds);
|
||||
|
||||
if (!(await hasFolderAccess(serverFolderIds, user))) {
|
||||
if (!(await folderPermissions(serverFolderIds!, user))) {
|
||||
throw ApiError.forbidden('');
|
||||
}
|
||||
|
||||
const serverFoldersFilter = serverFolderIds.map((serverFolderId: number) => {
|
||||
const serverFoldersFilter = serverFolderIds!.map((serverFolderId: number) => {
|
||||
return {
|
||||
serverFolder: { id: { equals: Number(serverFolderId) } },
|
||||
serverFolders: { some: { id: { equals: Number(serverFolderId) } } },
|
||||
};
|
||||
});
|
||||
|
||||
@@ -62,6 +67,7 @@ const getMany = async (
|
||||
data: albumArtists,
|
||||
paginationItems: {
|
||||
limit,
|
||||
page,
|
||||
startIndex,
|
||||
totalEntries,
|
||||
url: req.originalUrl,
|
||||
@@ -70,6 +76,6 @@ const getMany = async (
|
||||
};
|
||||
|
||||
export const albumArtistsService = {
|
||||
getMany,
|
||||
getOne,
|
||||
findById,
|
||||
findMany,
|
||||
};
|
||||
@@ -1,92 +0,0 @@
|
||||
import { Request } from 'express';
|
||||
import { prisma } from '../lib';
|
||||
import { OffsetPagination, User } from '../types/types';
|
||||
import {
|
||||
ApiError,
|
||||
ApiSuccess,
|
||||
hasFolderAccess,
|
||||
splitNumberString,
|
||||
} from '../utils';
|
||||
|
||||
const getOne = async (options: { id: number; user: User }) => {
|
||||
const { id, user } = options;
|
||||
|
||||
const album = await prisma.album.findUnique({
|
||||
include: {
|
||||
_count: true,
|
||||
albumArtist: true,
|
||||
genres: true,
|
||||
songs: {
|
||||
include: {
|
||||
album: true,
|
||||
artists: true,
|
||||
externals: true,
|
||||
genres: true,
|
||||
images: true,
|
||||
},
|
||||
orderBy: [{ disc: 'asc' }, { track: 'asc' }],
|
||||
},
|
||||
},
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!album) {
|
||||
throw ApiError.notFound('');
|
||||
}
|
||||
|
||||
if (!(await hasFolderAccess([album?.serverFolderId], user))) {
|
||||
throw ApiError.forbidden('');
|
||||
}
|
||||
|
||||
return ApiSuccess.ok({ data: album });
|
||||
};
|
||||
|
||||
const getMany = async (
|
||||
req: Request,
|
||||
options: { serverFolderIds: string; user: User } & OffsetPagination
|
||||
) => {
|
||||
const { user, limit, page, serverFolderIds: rServerFolderIds } = options;
|
||||
const serverFolderIds = splitNumberString(rServerFolderIds);
|
||||
|
||||
if (!(await hasFolderAccess(serverFolderIds, user))) {
|
||||
throw ApiError.forbidden('');
|
||||
}
|
||||
|
||||
const serverFoldersFilter = serverFolderIds.map((serverFolderId: number) => {
|
||||
return {
|
||||
ServerFolder: {
|
||||
id: { equals: Number(serverFolderId) },
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const startIndex = limit * page;
|
||||
const totalEntries = await prisma.album.count({
|
||||
where: { OR: serverFoldersFilter },
|
||||
});
|
||||
const albums = await prisma.album.findMany({
|
||||
include: {
|
||||
_count: { select: { songs: true } },
|
||||
albumArtist: true,
|
||||
genres: true,
|
||||
},
|
||||
skip: startIndex,
|
||||
take: limit,
|
||||
where: { OR: serverFoldersFilter },
|
||||
});
|
||||
|
||||
return ApiSuccess.ok({
|
||||
data: albums,
|
||||
paginationItems: {
|
||||
limit,
|
||||
startIndex,
|
||||
totalEntries,
|
||||
url: req.originalUrl,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const albumsService = {
|
||||
getMany,
|
||||
getOne,
|
||||
};
|
||||
148
src/server/services/albums.service.ts
Normal file
148
src/server/services/albums.service.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import { Request } from 'express';
|
||||
import { prisma } from '../lib';
|
||||
import { OffsetPagination, User } from '../types/types';
|
||||
import {
|
||||
ApiError,
|
||||
ApiSuccess,
|
||||
folderPermissions,
|
||||
getFolderPermissions,
|
||||
splitNumberString,
|
||||
} from '../utils';
|
||||
import { toRes } from './response';
|
||||
|
||||
const findById = async (options: {
|
||||
id: number;
|
||||
serverUrls?: string;
|
||||
user: User;
|
||||
}) => {
|
||||
const { id, user, serverUrls } = options;
|
||||
|
||||
const album = await prisma.album.findUnique({
|
||||
include: {
|
||||
_count: true,
|
||||
albumArtist: true,
|
||||
genres: true,
|
||||
images: true,
|
||||
server: {
|
||||
include: {
|
||||
serverUrls: serverUrls
|
||||
? { where: { id: { in: splitNumberString(serverUrls) } } }
|
||||
: true,
|
||||
},
|
||||
},
|
||||
serverFolders: true,
|
||||
songs: {
|
||||
include: {
|
||||
album: true,
|
||||
artists: true,
|
||||
externals: true,
|
||||
genres: true,
|
||||
images: true,
|
||||
},
|
||||
orderBy: [{ disc: 'asc' }, { track: 'asc' }],
|
||||
},
|
||||
},
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!album) {
|
||||
throw ApiError.notFound('');
|
||||
}
|
||||
|
||||
const serverFolderIds = album.serverFolders.map(
|
||||
(serverFolder) => serverFolder.id
|
||||
);
|
||||
|
||||
if (!(await folderPermissions(serverFolderIds, user))) {
|
||||
throw ApiError.forbidden('');
|
||||
}
|
||||
|
||||
return ApiSuccess.ok({ data: toRes.albums([album], user)[0] });
|
||||
};
|
||||
|
||||
const findMany = async (
|
||||
req: Request,
|
||||
options: {
|
||||
serverFolderIds?: string;
|
||||
serverUrls?: string;
|
||||
user: User;
|
||||
} & OffsetPagination
|
||||
) => {
|
||||
const {
|
||||
user,
|
||||
limit,
|
||||
page,
|
||||
serverFolderIds: rServerFolderIds,
|
||||
serverUrls,
|
||||
} = options;
|
||||
|
||||
const serverFolderIds = rServerFolderIds
|
||||
? splitNumberString(rServerFolderIds)
|
||||
: await getFolderPermissions(user);
|
||||
|
||||
if (!(await folderPermissions(serverFolderIds!, user))) {
|
||||
throw ApiError.forbidden('');
|
||||
}
|
||||
|
||||
const serverFoldersFilter = serverFolderIds!.map((serverFolderId: number) => {
|
||||
return {
|
||||
serverFolders: {
|
||||
some: {
|
||||
id: { equals: Number(serverFolderId) },
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const startIndex = limit * page;
|
||||
const totalEntries = await prisma.album.count({
|
||||
where: {
|
||||
OR: [...serverFoldersFilter],
|
||||
},
|
||||
});
|
||||
|
||||
const albums = await prisma.album.findMany({
|
||||
include: {
|
||||
_count: { select: { favorites: true, songs: true } },
|
||||
albumArtist: true,
|
||||
genres: true,
|
||||
images: true,
|
||||
server: {
|
||||
include: {
|
||||
serverUrls: serverUrls
|
||||
? { where: { id: { in: splitNumberString(serverUrls) } } }
|
||||
: true,
|
||||
},
|
||||
},
|
||||
songs: {
|
||||
include: {
|
||||
album: true,
|
||||
artists: true,
|
||||
externals: true,
|
||||
genres: true,
|
||||
images: true,
|
||||
},
|
||||
orderBy: [{ disc: 'asc' }, { track: 'asc' }],
|
||||
},
|
||||
},
|
||||
skip: startIndex,
|
||||
take: limit,
|
||||
where: { OR: serverFoldersFilter },
|
||||
});
|
||||
|
||||
return ApiSuccess.ok({
|
||||
data: toRes.albums(albums, user),
|
||||
paginationItems: {
|
||||
limit,
|
||||
page,
|
||||
startIndex,
|
||||
totalEntries,
|
||||
url: req.originalUrl,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const albumsService = {
|
||||
findById,
|
||||
findMany,
|
||||
};
|
||||
@@ -4,44 +4,50 @@ import { OffsetPagination, User } from '../types/types';
|
||||
import {
|
||||
ApiError,
|
||||
ApiSuccess,
|
||||
hasFolderAccess,
|
||||
folderPermissions,
|
||||
splitNumberString,
|
||||
} from '../utils';
|
||||
|
||||
const getOne = async (options: { id: number; user: User }) => {
|
||||
const findById = async (options: { id: number; user: User }) => {
|
||||
const { id, user } = options;
|
||||
|
||||
const album = await prisma.artist.findUnique({
|
||||
include: { genres: true },
|
||||
const artist = await prisma.artist.findUnique({
|
||||
include: { genres: true, serverFolders: true },
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!album) {
|
||||
if (!artist) {
|
||||
throw ApiError.notFound('');
|
||||
}
|
||||
|
||||
if (!(await hasFolderAccess([album?.serverFolderId], user))) {
|
||||
const serverFolderIds = artist.serverFolders.map(
|
||||
(serverFolder) => serverFolder.id
|
||||
);
|
||||
|
||||
if (!(await folderPermissions(serverFolderIds, user))) {
|
||||
throw ApiError.forbidden('');
|
||||
}
|
||||
|
||||
return ApiSuccess.ok({ data: album });
|
||||
return ApiSuccess.ok({ data: artist });
|
||||
};
|
||||
|
||||
const getMany = async (
|
||||
const findMany = async (
|
||||
req: Request,
|
||||
options: { serverFolderIds: string; user: User } & OffsetPagination
|
||||
) => {
|
||||
const { user, limit, page, serverFolderIds: rServerFolderIds } = options;
|
||||
const serverFolderIds = splitNumberString(rServerFolderIds);
|
||||
|
||||
if (!(await hasFolderAccess(serverFolderIds, user))) {
|
||||
if (!(await folderPermissions(serverFolderIds!, user))) {
|
||||
throw ApiError.forbidden('');
|
||||
}
|
||||
|
||||
const serverFoldersFilter = serverFolderIds.map((serverFolderId: number) => {
|
||||
const serverFoldersFilter = serverFolderIds!.map((serverFolderId: number) => {
|
||||
return {
|
||||
ServerFolder: {
|
||||
id: { equals: Number(serverFolderId) },
|
||||
serverFolders: {
|
||||
some: {
|
||||
id: { equals: Number(serverFolderId) },
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -61,6 +67,7 @@ const getMany = async (
|
||||
data: artists,
|
||||
paginationItems: {
|
||||
limit,
|
||||
page,
|
||||
startIndex,
|
||||
totalEntries,
|
||||
url: req.originalUrl,
|
||||
@@ -69,6 +76,6 @@ const getMany = async (
|
||||
};
|
||||
|
||||
export const artistsService = {
|
||||
getMany,
|
||||
getOne,
|
||||
findById,
|
||||
findMany,
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { prisma } from '../lib';
|
||||
import { ApiSuccess } from '../utils';
|
||||
import { ApiSuccess, randomString } from '../utils';
|
||||
import { ApiError } from '../utils/api-error';
|
||||
|
||||
const login = async (options: { username: string }) => {
|
||||
@@ -21,6 +21,7 @@ const register = async (options: { password: string; username: string }) => {
|
||||
const hashedPassword = await bcrypt.hash(password, 12);
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
deviceId: `${username}_${randomString(10)}`,
|
||||
enabled: false,
|
||||
password: hashedPassword,
|
||||
username,
|
||||
@@ -1,6 +1,6 @@
|
||||
export * from './auth-service';
|
||||
export * from './servers-service';
|
||||
export * from './album-artists-service';
|
||||
export * from './users-service';
|
||||
export * from './artists-service';
|
||||
export * from './albums-service';
|
||||
export * from './auth.service';
|
||||
export * from './servers.service';
|
||||
export * from './album-artists.service';
|
||||
export * from './users.service';
|
||||
export * from './artists.service';
|
||||
export * from './albums.service';
|
||||
|
||||
197
src/server/services/response.ts
Normal file
197
src/server/services/response.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
import { User } from '../types/types';
|
||||
import { getImageUrl } from '../utils';
|
||||
|
||||
const getSubsonicStreamUrl = (
|
||||
remoteId: string,
|
||||
url: string,
|
||||
token: string,
|
||||
deviceId: string
|
||||
) => {
|
||||
return (
|
||||
`${url}/rest/stream.view` +
|
||||
`?id=${remoteId}` +
|
||||
`&${token}` +
|
||||
`&v=1.13.0` +
|
||||
`&c=sonixd_${deviceId}`
|
||||
);
|
||||
};
|
||||
|
||||
const getJellyfinStreamUrl = (
|
||||
remoteId: string,
|
||||
url: string,
|
||||
token: string,
|
||||
userId: string,
|
||||
deviceId: string
|
||||
) => {
|
||||
return (
|
||||
`${url}/audio` +
|
||||
`/${remoteId}/universal` +
|
||||
`?userId=${userId}` +
|
||||
`&audioCodec=aac` +
|
||||
`&container=opus,mp3,aac,m4a,m4b,flac,wav,ogg` +
|
||||
`&transcodingContainer=ts` +
|
||||
`&transcodingProtocol=hls` +
|
||||
`&deviceId=sonixd_${deviceId}` +
|
||||
`&playSessionId=${deviceId}` +
|
||||
`&api_key=${token}`
|
||||
);
|
||||
};
|
||||
|
||||
const streamUrl = (
|
||||
serverType: string,
|
||||
args: {
|
||||
deviceId: string;
|
||||
remoteId: string;
|
||||
token: string;
|
||||
url: string;
|
||||
userId?: string;
|
||||
}
|
||||
) => {
|
||||
if (serverType === 'jellyfin') {
|
||||
return getJellyfinStreamUrl(
|
||||
args.remoteId,
|
||||
args.url,
|
||||
args.token,
|
||||
args.userId || '',
|
||||
args.deviceId
|
||||
);
|
||||
}
|
||||
|
||||
if (serverType === 'subsonic') {
|
||||
return getSubsonicStreamUrl(
|
||||
args.remoteId,
|
||||
args.url,
|
||||
args.token,
|
||||
args.deviceId
|
||||
);
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
const relatedArtists = (items: any[]) => {
|
||||
return (
|
||||
items?.map((item: any) => {
|
||||
return {
|
||||
deleted: item.deleted,
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
remoteId: item.remoteId,
|
||||
};
|
||||
}) || []
|
||||
);
|
||||
};
|
||||
|
||||
const relatedGenres = (genres: any[]) => {
|
||||
return (
|
||||
genres?.map((genre) => {
|
||||
return {
|
||||
id: genre.id,
|
||||
name: genre.name,
|
||||
};
|
||||
}) || []
|
||||
);
|
||||
};
|
||||
|
||||
const primaryImage = (
|
||||
images: any[],
|
||||
serverType: string,
|
||||
url: string,
|
||||
remoteId: string
|
||||
) => {
|
||||
const primaryImageId = images.find((i: any) => i.name === 'Primary')?.url;
|
||||
const image = !primaryImageId ? '' : getImageUrl(serverType, url, remoteId);
|
||||
|
||||
return image;
|
||||
};
|
||||
|
||||
const songs = (
|
||||
items: any[],
|
||||
options: {
|
||||
deviceId: string;
|
||||
serverType?: string;
|
||||
token: string;
|
||||
url?: string;
|
||||
userId: string;
|
||||
}
|
||||
) => {
|
||||
return (
|
||||
items?.map((item: any) => {
|
||||
const serverType = options.serverType
|
||||
? options?.serverType
|
||||
: item.server.serverType;
|
||||
|
||||
const url = options.url ? options.url : item.server.serverUrls[0];
|
||||
|
||||
return {
|
||||
albumId: item.albumId,
|
||||
artistName: item.artistName,
|
||||
artists: relatedArtists(item.artists),
|
||||
bitRate: item.bitRate,
|
||||
container: item.container,
|
||||
createdAt: item.createdAt,
|
||||
date: item.date,
|
||||
deleted: item.deleted,
|
||||
disc: item.disc,
|
||||
duration: item.duration,
|
||||
genres: relatedGenres(item.genres),
|
||||
id: item.id,
|
||||
image: primaryImage(item.images, serverType, url, item.remoteId),
|
||||
name: item.name,
|
||||
remoteCreatedAt: item.remoteCreatedAt,
|
||||
remoteId: item.remoteId,
|
||||
serverId: item.serverId,
|
||||
streamUrl: streamUrl(serverType, {
|
||||
deviceId: options.deviceId,
|
||||
remoteId: item.remoteId,
|
||||
token: options.token,
|
||||
url,
|
||||
userId: options.userId,
|
||||
}),
|
||||
track: item.track,
|
||||
updatedAt: item.updatedAt,
|
||||
year: item.year,
|
||||
};
|
||||
}) || []
|
||||
);
|
||||
};
|
||||
|
||||
const albums = (items: any[], user: User) => {
|
||||
return (
|
||||
items?.map((item: any) => {
|
||||
const { serverType, token, remoteUserId } = item.server;
|
||||
const { url } = item.server.serverUrls[0];
|
||||
|
||||
return {
|
||||
albumArtistId: item.albumArtistId,
|
||||
createdAt: item.createdAt,
|
||||
dateCreated: item.date,
|
||||
deleted: item.deleted,
|
||||
genres: relatedGenres(item.genres),
|
||||
id: item.id,
|
||||
image: primaryImage(item.images, serverType, url, item.remoteId),
|
||||
name: item.name,
|
||||
remoteCreatedAt: item.remoteCreatedAt,
|
||||
remoteId: item.remoteId,
|
||||
serverFolderId: item.serverFolderId,
|
||||
serverType,
|
||||
songCount: item._count.songs,
|
||||
songs: songs(item.songs, {
|
||||
deviceId: user.deviceId,
|
||||
serverType,
|
||||
token,
|
||||
url,
|
||||
userId: remoteUserId,
|
||||
}),
|
||||
updatedAt: item.updatedAt,
|
||||
year: item.year,
|
||||
};
|
||||
}) || []
|
||||
);
|
||||
};
|
||||
|
||||
export const toRes = {
|
||||
albums,
|
||||
songs,
|
||||
};
|
||||
@@ -8,11 +8,11 @@ import {
|
||||
import { User } from '../types/types';
|
||||
import { ApiError, ApiSuccess, splitNumberString } from '../utils';
|
||||
|
||||
const getOne = async (user: User, options: { id: number }) => {
|
||||
const findById = async (user: User, options: { id: number }) => {
|
||||
const { id } = options;
|
||||
const server = await prisma.server.findUnique({
|
||||
include: {
|
||||
serverFolder: user.isAdmin
|
||||
serverFolders: user.isAdmin
|
||||
? true
|
||||
: {
|
||||
where: {
|
||||
@@ -30,24 +30,24 @@ const getOne = async (user: User, options: { id: number }) => {
|
||||
throw ApiError.notFound('');
|
||||
}
|
||||
|
||||
if (!user.isAdmin && server.serverFolder.length === 0) {
|
||||
if (!user.isAdmin && server.serverFolders.length === 0) {
|
||||
throw ApiError.forbidden('');
|
||||
}
|
||||
|
||||
return ApiSuccess.ok({ data: server });
|
||||
};
|
||||
|
||||
const getMany = async (user: User) => {
|
||||
const findMany = async (user: User) => {
|
||||
let servers;
|
||||
|
||||
if (user.isAdmin) {
|
||||
servers = await prisma.server.findMany({
|
||||
include: { serverFolder: true },
|
||||
include: { serverFolders: true },
|
||||
});
|
||||
} else {
|
||||
servers = await prisma.server.findMany({
|
||||
include: {
|
||||
serverFolder: {
|
||||
serverFolders: {
|
||||
where: {
|
||||
OR: [
|
||||
{ isPublic: true },
|
||||
@@ -56,7 +56,7 @@ const getMany = async (user: User) => {
|
||||
},
|
||||
},
|
||||
},
|
||||
where: { serverFolder: { some: { isPublic: true } } },
|
||||
where: { serverFolders: { some: { isPublic: true } } },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,7 +71,13 @@ const create = async (options: {
|
||||
url: string;
|
||||
username: string;
|
||||
}) => {
|
||||
const server = await prisma.server.create({ data: options });
|
||||
const checkDuplicate = await prisma.server.findUnique({
|
||||
where: { url: options.url },
|
||||
});
|
||||
|
||||
if (checkDuplicate) {
|
||||
throw ApiError.conflict('Server already exists.');
|
||||
}
|
||||
|
||||
let musicFoldersData: {
|
||||
name: string;
|
||||
@@ -80,7 +86,26 @@ const create = async (options: {
|
||||
}[] = [];
|
||||
|
||||
if (options.serverType === 'subsonic') {
|
||||
const musicFoldersRes = await subsonicApi.getMusicFolders(server);
|
||||
const musicFoldersRes = await subsonicApi.getMusicFolders({
|
||||
token: options.token,
|
||||
url: options.url,
|
||||
});
|
||||
|
||||
if (!musicFoldersRes) {
|
||||
throw ApiError.badRequest('Server is inaccessible.');
|
||||
}
|
||||
|
||||
const server = await prisma.server.create({
|
||||
data: {
|
||||
name: options.name,
|
||||
remoteUserId: options.remoteUserId,
|
||||
serverType: options.serverType,
|
||||
token: options.token,
|
||||
url: options.url,
|
||||
username: options.username,
|
||||
},
|
||||
});
|
||||
|
||||
musicFoldersData = musicFoldersRes.map((musicFolder) => {
|
||||
return {
|
||||
name: musicFolder.name,
|
||||
@@ -88,10 +113,46 @@ const create = async (options: {
|
||||
serverId: server.id,
|
||||
};
|
||||
});
|
||||
|
||||
musicFoldersData.forEach(async (musicFolder) => {
|
||||
await prisma.serverFolder.upsert({
|
||||
create: musicFolder,
|
||||
update: { name: musicFolder.name },
|
||||
where: {
|
||||
uniqueServerFolderId: {
|
||||
remoteId: musicFolder.remoteId,
|
||||
serverId: musicFolder.serverId,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return ApiSuccess.ok({ data: { ...server } });
|
||||
}
|
||||
|
||||
if (options.serverType === 'jellyfin') {
|
||||
const musicFoldersRes = await jellyfinApi.getMusicFolders(server);
|
||||
const musicFoldersRes = await jellyfinApi.getMusicFolders({
|
||||
remoteUserId: options.remoteUserId,
|
||||
token: options.token,
|
||||
url: options.url,
|
||||
});
|
||||
|
||||
if (!musicFoldersRes) {
|
||||
throw ApiError.badRequest('Server is inaccessible.');
|
||||
}
|
||||
|
||||
const server = await prisma.server.create({
|
||||
data: {
|
||||
name: options.name,
|
||||
remoteUserId: options.remoteUserId,
|
||||
serverType: options.serverType,
|
||||
serverUrls: { create: { url: options.url } },
|
||||
token: options.token,
|
||||
url: options.url,
|
||||
username: options.username,
|
||||
},
|
||||
});
|
||||
|
||||
musicFoldersData = musicFoldersRes.map((musicFolder) => {
|
||||
return {
|
||||
name: musicFolder.Name,
|
||||
@@ -99,22 +160,24 @@ const create = async (options: {
|
||||
serverId: server.id,
|
||||
};
|
||||
});
|
||||
|
||||
musicFoldersData.forEach(async (musicFolder) => {
|
||||
await prisma.serverFolder.upsert({
|
||||
create: musicFolder,
|
||||
update: { name: musicFolder.name },
|
||||
where: {
|
||||
uniqueServerFolderId: {
|
||||
remoteId: musicFolder.remoteId,
|
||||
serverId: musicFolder.serverId,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return ApiSuccess.ok({ data: { ...server } });
|
||||
}
|
||||
|
||||
musicFoldersData.forEach(async (musicFolder) => {
|
||||
await prisma.serverFolder.upsert({
|
||||
create: musicFolder,
|
||||
update: { name: musicFolder.name },
|
||||
where: {
|
||||
uniqueServerFolderId: {
|
||||
remoteId: musicFolder.remoteId,
|
||||
serverId: musicFolder.serverId,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return ApiSuccess.ok({ data: { ...server } });
|
||||
return ApiSuccess.ok({ data: {} });
|
||||
};
|
||||
|
||||
const refresh = async (options: { id: number }) => {
|
||||
@@ -153,6 +216,8 @@ const refresh = async (options: { id: number }) => {
|
||||
});
|
||||
}
|
||||
|
||||
// mark as deleted if not found
|
||||
|
||||
musicFoldersData.forEach(async (musicFolder) => {
|
||||
await prisma.serverFolder.upsert({
|
||||
create: musicFolder,
|
||||
@@ -176,7 +241,7 @@ const fullScan = async (options: {
|
||||
}) => {
|
||||
const { id, serverFolderIds } = options;
|
||||
const server = await prisma.server.findUnique({
|
||||
include: { serverFolder: true },
|
||||
include: { serverFolders: true },
|
||||
where: { id },
|
||||
});
|
||||
|
||||
@@ -187,11 +252,11 @@ const fullScan = async (options: {
|
||||
let serverFolders;
|
||||
if (serverFolderIds) {
|
||||
const selectedServerFolderIds = splitNumberString(serverFolderIds);
|
||||
serverFolders = server.serverFolder.filter((folder) =>
|
||||
serverFolders = server.serverFolders.filter((folder) =>
|
||||
selectedServerFolderIds?.includes(folder.id)
|
||||
);
|
||||
} else {
|
||||
serverFolders = server.serverFolder;
|
||||
serverFolders = server.serverFolders;
|
||||
}
|
||||
|
||||
if (server.serverType === 'jellyfin') {
|
||||
@@ -229,8 +294,8 @@ const fullScan = async (options: {
|
||||
|
||||
export const serversService = {
|
||||
create,
|
||||
findById,
|
||||
findMany,
|
||||
fullScan,
|
||||
getMany,
|
||||
getOne,
|
||||
refresh,
|
||||
};
|
||||
120
src/server/services/songs.service.ts
Normal file
120
src/server/services/songs.service.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { Request } from 'express';
|
||||
import { prisma } from '../lib';
|
||||
import { OffsetPagination, User } from '../types/types';
|
||||
import {
|
||||
ApiError,
|
||||
ApiSuccess,
|
||||
folderPermissions,
|
||||
splitNumberString,
|
||||
} from '../utils';
|
||||
import { toRes } from './response';
|
||||
import { SongRequestParams } from './types';
|
||||
|
||||
const findById = async (options: { id: number; user: User }) => {
|
||||
const { id, user } = options;
|
||||
|
||||
const album = await prisma.album.findUnique({
|
||||
include: {
|
||||
_count: true,
|
||||
albumArtist: true,
|
||||
genres: true,
|
||||
songs: {
|
||||
include: {
|
||||
album: true,
|
||||
artists: true,
|
||||
externals: true,
|
||||
genres: true,
|
||||
images: true,
|
||||
},
|
||||
orderBy: [{ disc: 'asc' }, { track: 'asc' }],
|
||||
},
|
||||
},
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!album) {
|
||||
throw ApiError.notFound('');
|
||||
}
|
||||
|
||||
// if (!(await folderPermissions([album?.serverFolderId], user))) {
|
||||
// throw ApiError.forbidden('');
|
||||
// }
|
||||
|
||||
return ApiSuccess.ok({ data: album });
|
||||
};
|
||||
|
||||
const findMany = async (
|
||||
req: Request,
|
||||
options: SongRequestParams & { user: User }
|
||||
) => {
|
||||
const {
|
||||
albumIds: rawAlbumIds,
|
||||
artistIds: rawArtistIds,
|
||||
songIds: rawSongIds,
|
||||
user,
|
||||
limit,
|
||||
page,
|
||||
serverFolderIds: rServerFolderIds,
|
||||
} = options;
|
||||
const serverFolderIds = splitNumberString(rServerFolderIds);
|
||||
const albumIds = splitNumberString(rawAlbumIds);
|
||||
const artistIds = splitNumberString(rawArtistIds);
|
||||
const songIds = splitNumberString(rawSongIds);
|
||||
|
||||
if (serverFolderIds) {
|
||||
if (!(await folderPermissions(serverFolderIds, user))) {
|
||||
throw ApiError.forbidden('');
|
||||
}
|
||||
}
|
||||
|
||||
// const serverFoldersFilter = serverFolderIds!.map((serverFolderId: number) => {
|
||||
// return { serverFolders: { id: { equals: serverFolderId } } };
|
||||
// });
|
||||
|
||||
const serverFoldersFilter = {
|
||||
serverFolders: { some: { id: { in: serverFolderIds } } },
|
||||
};
|
||||
|
||||
const startIndex = limit * page;
|
||||
|
||||
const [totalEntries, songs] = await prisma.$transaction([
|
||||
prisma.song.count({
|
||||
where: {
|
||||
OR: [
|
||||
serverFoldersFilter,
|
||||
{
|
||||
albumId: { in: albumIds },
|
||||
id: { in: songIds },
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
prisma.song.findMany({
|
||||
include: {
|
||||
_count: { select: { favorites: true } },
|
||||
genres: true,
|
||||
images: true,
|
||||
serverFolders: { include: { server: true } },
|
||||
},
|
||||
skip: startIndex,
|
||||
take: limit,
|
||||
where: { OR: serverFoldersFilter },
|
||||
}),
|
||||
]);
|
||||
|
||||
return ApiSuccess.ok({
|
||||
data: songs,
|
||||
paginationItems: {
|
||||
limit,
|
||||
page,
|
||||
startIndex,
|
||||
totalEntries,
|
||||
url: req.originalUrl,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const songsService = {
|
||||
findById,
|
||||
findMany,
|
||||
};
|
||||
8
src/server/services/types.ts
Normal file
8
src/server/services/types.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { OffsetPagination } from '../types/types';
|
||||
|
||||
export interface SongRequestParams extends OffsetPagination {
|
||||
albumIds?: string;
|
||||
artistIds?: string;
|
||||
serverFolderIds: string;
|
||||
songIds?: string;
|
||||
}
|
||||
91
src/server/utils/folder-permissions.ts
Normal file
91
src/server/utils/folder-permissions.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { prisma } from '../lib';
|
||||
import { User } from '../types/types';
|
||||
|
||||
export enum Roles {
|
||||
NONE = 0,
|
||||
GUEST = 1,
|
||||
USER = 2,
|
||||
ADMIN = 4,
|
||||
SUPERADMIN = 8,
|
||||
}
|
||||
|
||||
export enum FolderRoles {
|
||||
NONE = 0,
|
||||
READ = 1,
|
||||
WRITE = 2,
|
||||
ADMIN = 4,
|
||||
}
|
||||
|
||||
export const folderPermissions = async (serverFolderIds: any[], user: User) => {
|
||||
if (user.isAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const serverFoldersWithAccess = await prisma.serverFolder.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
isPublic: true,
|
||||
},
|
||||
{
|
||||
AND: [
|
||||
{ isPublic: false },
|
||||
{
|
||||
serverFolderPermissions: {
|
||||
some: { userId: { equals: user.id } },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const serverFoldersWithAccessIds = serverFoldersWithAccess.map(
|
||||
(serverFolder) => serverFolder.id
|
||||
);
|
||||
|
||||
const hasAccess = serverFolderIds.every((id) =>
|
||||
serverFoldersWithAccessIds.includes(id)
|
||||
);
|
||||
|
||||
return hasAccess;
|
||||
};
|
||||
|
||||
export const getFolderPermissions = async (user: User) => {
|
||||
if (user.isAdmin) {
|
||||
const serverFoldersWithAccess = await prisma.serverFolder.findMany();
|
||||
|
||||
const serverFoldersWithAccessIds = serverFoldersWithAccess.map(
|
||||
(serverFolder) => serverFolder.id
|
||||
);
|
||||
|
||||
return serverFoldersWithAccessIds;
|
||||
}
|
||||
|
||||
const serverFoldersWithAccess = await prisma.serverFolder.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
isPublic: true,
|
||||
},
|
||||
{
|
||||
AND: [
|
||||
{ isPublic: false },
|
||||
{
|
||||
serverFolderPermissions: {
|
||||
some: { userId: { equals: user.id } },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const serverFoldersWithAccessIds = serverFoldersWithAccess.map(
|
||||
(serverFolder) => serverFolder.id
|
||||
);
|
||||
|
||||
return serverFoldersWithAccessIds;
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
import { prisma } from '../lib';
|
||||
import { User } from '../types/types';
|
||||
|
||||
export const hasFolderAccess = async (serverFolderIds: any[], user: User) => {
|
||||
if (user.isAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const serverFoldersWithAccess = await prisma.serverFolder.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
isPublic: true,
|
||||
},
|
||||
{
|
||||
AND: [
|
||||
{ isPublic: false },
|
||||
{
|
||||
serverFolderPermissions: {
|
||||
some: { userId: { equals: user.id } },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const serverFoldersWithAccessIds = serverFoldersWithAccess.map(
|
||||
(serverFolder) => serverFolder.id
|
||||
);
|
||||
|
||||
const hasAccess = serverFolderIds.every((id) =>
|
||||
serverFoldersWithAccessIds.includes(id)
|
||||
);
|
||||
|
||||
return hasAccess;
|
||||
};
|
||||
@@ -4,7 +4,7 @@ export * from './get-success-response';
|
||||
export * from './group-by-property';
|
||||
export * from './split-number-string';
|
||||
export * from './split-text-string';
|
||||
export * from './has-folder-access';
|
||||
export * from './folder-permissions';
|
||||
export * from './is-array-equal';
|
||||
export * from './is-json-string';
|
||||
export * from './validate-request';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export const randomString = () => {
|
||||
export const randomString = (length: number) => {
|
||||
const charSet =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let string = '';
|
||||
for (let i = 0; i < 12; i += 1) {
|
||||
for (let i = 0; i < length; i += 1) {
|
||||
const randomPoz = Math.floor(Math.random() * charSet.length);
|
||||
string += charSet.substring(randomPoz, randomPoz + 1);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
export const splitNumberString = (string: string, delimiter = ',') => {
|
||||
export const splitNumberString = (string?: string, delimiter = ',') => {
|
||||
if (!string) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return string.split(delimiter).map((s: string) => {
|
||||
return Number(s);
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
||||
export const paginationValidation = {
|
||||
limit: z.preprocess(
|
||||
(a) => parseInt(z.string().parse(a), 10),
|
||||
z.number().max(1000)
|
||||
z.number().min(0).max(1000)
|
||||
),
|
||||
page: z.preprocess((a) => parseInt(z.string().parse(a), 10), z.number()),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user