mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-08 14:43:24 +00:00
refactor: split links controller into multiple controllers
This commit is contained in:
@@ -99,7 +99,7 @@ type FaviconGetHead = {
|
|||||||
type LinksCreateGetHead = {
|
type LinksCreateGetHead = {
|
||||||
request: unknown;
|
request: unknown;
|
||||||
response: MakeTuyauResponse<
|
response: MakeTuyauResponse<
|
||||||
import('../app/links/controllers/links_controller.ts').default['showCreatePage'],
|
import('../app/links/controllers/delete_link_controller.js').default['showCreatePage'],
|
||||||
false
|
false
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
@@ -110,14 +110,14 @@ type LinksPost = {
|
|||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
response: MakeTuyauResponse<
|
response: MakeTuyauResponse<
|
||||||
import('../app/links/controllers/links_controller.ts').default['store'],
|
import('../app/links/controllers/delete_link_controller.js').default['store'],
|
||||||
true
|
true
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
type LinksEditGetHead = {
|
type LinksEditGetHead = {
|
||||||
request: unknown;
|
request: unknown;
|
||||||
response: MakeTuyauResponse<
|
response: MakeTuyauResponse<
|
||||||
import('../app/links/controllers/links_controller.ts').default['showEditPage'],
|
import('../app/links/controllers/delete_link_controller.js').default['showEditPage'],
|
||||||
false
|
false
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
@@ -128,7 +128,7 @@ type LinksIdPut = {
|
|||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
response: MakeTuyauResponse<
|
response: MakeTuyauResponse<
|
||||||
import('../app/links/controllers/links_controller.ts').default['update'],
|
import('../app/links/controllers/delete_link_controller.js').default['update'],
|
||||||
true
|
true
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
@@ -139,14 +139,14 @@ type LinksIdFavoritePut = {
|
|||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
response: MakeTuyauResponse<
|
response: MakeTuyauResponse<
|
||||||
import('../app/links/controllers/links_controller.ts').default['toggleFavorite'],
|
import('../app/links/controllers/delete_link_controller.js').default['toggleFavorite'],
|
||||||
true
|
true
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
type LinksDeleteGetHead = {
|
type LinksDeleteGetHead = {
|
||||||
request: unknown;
|
request: unknown;
|
||||||
response: MakeTuyauResponse<
|
response: MakeTuyauResponse<
|
||||||
import('../app/links/controllers/links_controller.ts').default['showDeletePage'],
|
import('../app/links/controllers/delete_link_controller.js').default['showDeletePage'],
|
||||||
false
|
false
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
@@ -157,7 +157,7 @@ type LinksIdDelete = {
|
|||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
response: MakeTuyauResponse<
|
response: MakeTuyauResponse<
|
||||||
import('../app/links/controllers/links_controller.ts').default['delete'],
|
import('../app/links/controllers/delete_link_controller.js').default['execute'],
|
||||||
true
|
true
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import AuthController from '#auth/controllers/auth_controller';
|
import AuthController from '#auth/controllers/auth_controller';
|
||||||
import CollectionsController from '#collections/controllers/show_collections_controller';
|
import { CollectionService } from '#collections/services/collection_service';
|
||||||
import LinksController from '#links/controllers/links_controller';
|
import LinksController from '#links/controllers/delete_link_controller';
|
||||||
import User from '#user/models/user';
|
import User from '#user/models/user';
|
||||||
import { inject } from '@adonisjs/core';
|
import { inject } from '@adonisjs/core';
|
||||||
import { HttpContext } from '@adonisjs/core/http';
|
import { HttpContext } from '@adonisjs/core/http';
|
||||||
@@ -28,14 +28,14 @@ export default class AdminController {
|
|||||||
constructor(
|
constructor(
|
||||||
protected usersController: AuthController,
|
protected usersController: AuthController,
|
||||||
protected linksController: LinksController,
|
protected linksController: LinksController,
|
||||||
protected collectionsController: CollectionsController
|
protected collectionService: CollectionService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async index({ inertia }: HttpContext) {
|
async index({ inertia }: HttpContext) {
|
||||||
const users = await this.usersController.getAllUsersWithTotalRelations();
|
const users = await this.usersController.getAllUsersWithTotalRelations();
|
||||||
const linksCount = await this.linksController.getTotalLinksCount();
|
const linksCount = await this.linksController.getTotalLinksCount();
|
||||||
const collectionsCount =
|
const collectionsCount =
|
||||||
await this.collectionsController.getTotalCollectionsCount();
|
await this.collectionService.getTotalCollectionsCount();
|
||||||
|
|
||||||
return inertia.render('admin/dashboard', {
|
return inertia.render('admin/dashboard', {
|
||||||
users: users.map((user) => new UserWithRelationCountDto(user).toJson()),
|
users: users.map((user) => new UserWithRelationCountDto(user).toJson()),
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
import Collection from '#collections/models/collection';
|
|
||||||
import { HttpContext } from '@adonisjs/core/http';
|
|
||||||
import vine from '@vinejs/vine';
|
|
||||||
|
|
||||||
export default class BaseCollectionController {
|
|
||||||
protected collectionIdValidator = vine.compile(
|
|
||||||
vine.object({
|
|
||||||
collectionId: vine.number().positive().optional(),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
async validateCollectionId(collectionIdRequired: boolean = true) {
|
|
||||||
const ctx = HttpContext.getOrFail();
|
|
||||||
const { collectionId } = await ctx.request.validateUsing(
|
|
||||||
this.collectionIdValidator
|
|
||||||
);
|
|
||||||
if (!collectionId && collectionIdRequired) {
|
|
||||||
console.log('redirecting to dashboard');
|
|
||||||
ctx.response.redirectToNamedRoute('dashboard');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
console.log('collectionId', collectionId);
|
|
||||||
return collectionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
redirectToCollectionId(collectionId: Collection['id']) {
|
|
||||||
const ctx = HttpContext.getOrFail();
|
|
||||||
return ctx.response.redirectToNamedRoute('dashboard', {
|
|
||||||
qs: { collectionId },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,15 @@
|
|||||||
import BaseCollectionController from '#collections/controllers/base_collection_controller';
|
|
||||||
import { CollectionService } from '#collections/services/collection_service';
|
import { CollectionService } from '#collections/services/collection_service';
|
||||||
import { createCollectionValidator } from '#collections/validators/create_collection_validator';
|
import { createCollectionValidator } from '#collections/validators/create_collection_validator';
|
||||||
import { inject } from '@adonisjs/core';
|
import { inject } from '@adonisjs/core';
|
||||||
import { type HttpContext } from '@adonisjs/core/http';
|
import { type HttpContext } from '@adonisjs/core/http';
|
||||||
|
|
||||||
@inject()
|
@inject()
|
||||||
export default class CreateCollectionController extends BaseCollectionController {
|
export default class CreateCollectionController {
|
||||||
constructor(private collectionService: CollectionService) {
|
constructor(private collectionService: CollectionService) {}
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
async render({ inertia }: HttpContext) {
|
async render({ inertia }: HttpContext) {
|
||||||
const collections = await this.collectionService.getCollectionsByAuthorId();
|
const collections =
|
||||||
|
await this.collectionService.getCollectionsForAuthenticatedUser();
|
||||||
return inertia.render('collections/create', {
|
return inertia.render('collections/create', {
|
||||||
disableHomeLink: collections.length === 0,
|
disableHomeLink: collections.length === 0,
|
||||||
});
|
});
|
||||||
@@ -20,6 +18,6 @@ export default class CreateCollectionController extends BaseCollectionController
|
|||||||
async execute({ request }: HttpContext) {
|
async execute({ request }: HttpContext) {
|
||||||
const payload = await request.validateUsing(createCollectionValidator);
|
const payload = await request.validateUsing(createCollectionValidator);
|
||||||
const collection = await this.collectionService.createCollection(payload);
|
const collection = await this.collectionService.createCollection(payload);
|
||||||
return this.redirectToCollectionId(collection.id);
|
return this.collectionService.redirectToCollectionId(collection.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
import BaseCollectionController from '#collections/controllers/base_collection_controller';
|
|
||||||
import { CollectionService } from '#collections/services/collection_service';
|
import { CollectionService } from '#collections/services/collection_service';
|
||||||
import { deleteCollectionValidator } from '#collections/validators/delete_collection_validator';
|
import { deleteCollectionValidator } from '#collections/validators/delete_collection_validator';
|
||||||
import { inject } from '@adonisjs/core';
|
import { inject } from '@adonisjs/core';
|
||||||
import { HttpContext } from '@adonisjs/core/http';
|
import { HttpContext } from '@adonisjs/core/http';
|
||||||
|
|
||||||
@inject()
|
@inject()
|
||||||
export default class DeleteCollectionController extends BaseCollectionController {
|
export default class DeleteCollectionController {
|
||||||
constructor(private collectionService: CollectionService) {
|
constructor(private collectionService: CollectionService) {}
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
async render({ inertia }: HttpContext) {
|
async render({ inertia }: HttpContext) {
|
||||||
const collectionId = await this.validateCollectionId();
|
const collectionId = await this.collectionService.validateCollectionId();
|
||||||
if (!collectionId) return;
|
if (!collectionId) return;
|
||||||
|
|
||||||
const collection =
|
const collection =
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
import BaseCollectionController from '#collections/controllers/base_collection_controller';
|
|
||||||
import { CollectionService } from '#collections/services/collection_service';
|
import { CollectionService } from '#collections/services/collection_service';
|
||||||
import { inject } from '@adonisjs/core';
|
import { inject } from '@adonisjs/core';
|
||||||
import type { HttpContext } from '@adonisjs/core/http';
|
import type { HttpContext } from '@adonisjs/core/http';
|
||||||
|
|
||||||
@inject()
|
@inject()
|
||||||
export default class ShowCollectionsController extends BaseCollectionController {
|
export default class ShowCollectionsController {
|
||||||
constructor(private collectionService: CollectionService) {
|
constructor(private collectionService: CollectionService) {}
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dashboard
|
// Dashboard
|
||||||
async render({ inertia, response }: HttpContext) {
|
async render({ inertia, response }: HttpContext) {
|
||||||
const activeCollectionId = await this.validateCollectionId(false);
|
const activeCollectionId =
|
||||||
const collections = await this.collectionService.getCollectionsByAuthorId();
|
await this.collectionService.validateCollectionId(false);
|
||||||
|
const collections =
|
||||||
|
await this.collectionService.getCollectionsForAuthenticatedUser();
|
||||||
if (collections.length === 0) {
|
if (collections.length === 0) {
|
||||||
return response.redirectToNamedRoute('collection.create-form');
|
return response.redirectToNamedRoute('collection.create-form');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
import BaseCollectionController from '#collections/controllers/base_collection_controller';
|
|
||||||
import { CollectionService } from '#collections/services/collection_service';
|
import { CollectionService } from '#collections/services/collection_service';
|
||||||
import { updateCollectionValidator } from '#collections/validators/update_collection_validator';
|
import { updateCollectionValidator } from '#collections/validators/update_collection_validator';
|
||||||
import { inject } from '@adonisjs/core';
|
import { inject } from '@adonisjs/core';
|
||||||
import { HttpContext } from '@adonisjs/core/http';
|
import { HttpContext } from '@adonisjs/core/http';
|
||||||
|
|
||||||
@inject()
|
@inject()
|
||||||
export default class UpdateCollectionController extends BaseCollectionController {
|
export default class UpdateCollectionController {
|
||||||
constructor(private collectionService: CollectionService) {
|
constructor(private collectionService: CollectionService) {}
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
async render({ inertia }: HttpContext) {
|
async render({ inertia }: HttpContext) {
|
||||||
const collectionId = await this.validateCollectionId();
|
const collectionId = await this.collectionService.validateCollectionId();
|
||||||
if (!collectionId) return;
|
if (!collectionId) return;
|
||||||
|
|
||||||
const collection =
|
const collection =
|
||||||
@@ -28,6 +25,6 @@ export default class UpdateCollectionController extends BaseCollectionController
|
|||||||
} = await request.validateUsing(updateCollectionValidator);
|
} = await request.validateUsing(updateCollectionValidator);
|
||||||
|
|
||||||
await this.collectionService.updateCollection(collectionId, payload);
|
await this.collectionService.updateCollection(collectionId, payload);
|
||||||
return this.redirectToCollectionId(collectionId);
|
return this.collectionService.redirectToCollectionId(collectionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Visibility } from '#collections/enums/visibility';
|
import { Visibility } from '#collections/enums/visibility';
|
||||||
import Collection from '#collections/models/collection';
|
import Collection from '#collections/models/collection';
|
||||||
|
import { collectionIdValidator } from '#collections/validators/collection_id_validator';
|
||||||
import { HttpContext } from '@adonisjs/core/http';
|
import { HttpContext } from '@adonisjs/core/http';
|
||||||
import db from '@adonisjs/lucid/services/db';
|
import db from '@adonisjs/lucid/services/db';
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ export class CollectionService {
|
|||||||
.firstOrFail();
|
.firstOrFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCollectionsByAuthorId() {
|
async getCollectionsForAuthenticatedUser() {
|
||||||
const context = this.getAuthContext();
|
const context = this.getAuthContext();
|
||||||
return await Collection.query()
|
return await Collection.query()
|
||||||
.where('author_id', context.auth.user!.id)
|
.where('author_id', context.auth.user!.id)
|
||||||
@@ -67,4 +68,28 @@ export class CollectionService {
|
|||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async validateCollectionId(collectionIdRequired: boolean = true) {
|
||||||
|
const ctx = HttpContext.getOrFail();
|
||||||
|
const { collectionId } = await ctx.request.validateUsing(
|
||||||
|
collectionIdValidator
|
||||||
|
);
|
||||||
|
if (!collectionId && collectionIdRequired) {
|
||||||
|
this.redirectToDashboard();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return collectionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectToCollectionId(collectionId: Collection['id']) {
|
||||||
|
const ctx = HttpContext.getOrFail();
|
||||||
|
return ctx.response.redirectToNamedRoute('dashboard', {
|
||||||
|
qs: { collectionId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectToDashboard() {
|
||||||
|
const ctx = HttpContext.getOrFail();
|
||||||
|
return ctx.response.redirectToNamedRoute('dashboard');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
app/collections/validators/collection_id_validator.ts
Normal file
7
app/collections/validators/collection_id_validator.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import vine from '@vinejs/vine';
|
||||||
|
|
||||||
|
export const collectionIdValidator = vine.compile(
|
||||||
|
vine.object({
|
||||||
|
collectionId: vine.number().positive().optional(),
|
||||||
|
})
|
||||||
|
);
|
||||||
30
app/links/controllers/create_link_controller.ts
Normal file
30
app/links/controllers/create_link_controller.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { CollectionService } from '#collections/services/collection_service';
|
||||||
|
import { LinkService } from '#links/services/link_service';
|
||||||
|
import { createLinkValidator } from '#links/validators/create_link_validator';
|
||||||
|
import { inject } from '@adonisjs/core';
|
||||||
|
import { HttpContext } from '@adonisjs/core/http';
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
export default class CreateLinkController {
|
||||||
|
constructor(
|
||||||
|
private linkService: LinkService,
|
||||||
|
private collectionsService: CollectionService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async render({ inertia }: HttpContext) {
|
||||||
|
const collections =
|
||||||
|
await this.collectionsService.getCollectionsForAuthenticatedUser();
|
||||||
|
return inertia.render('links/create', { collections });
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute({ request }: HttpContext) {
|
||||||
|
const { collectionId, ...payload } =
|
||||||
|
await request.validateUsing(createLinkValidator);
|
||||||
|
|
||||||
|
await this.linkService.createLink({
|
||||||
|
...payload,
|
||||||
|
collectionId,
|
||||||
|
});
|
||||||
|
return this.collectionsService.redirectToCollectionId(collectionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/links/controllers/delete_link_controller.ts
Normal file
39
app/links/controllers/delete_link_controller.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { CollectionService } from '#collections/services/collection_service';
|
||||||
|
import { LinkService } from '#links/services/link_service';
|
||||||
|
import { deleteLinkValidator } from '#links/validators/delete_link_validator';
|
||||||
|
import { inject } from '@adonisjs/core';
|
||||||
|
import { HttpContext } from '@adonisjs/core/http';
|
||||||
|
import db from '@adonisjs/lucid/services/db';
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
export default class DeleteLinkController {
|
||||||
|
constructor(
|
||||||
|
protected collectionsService: CollectionService,
|
||||||
|
protected linkService: LinkService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async render({ auth, inertia, request }: HttpContext) {
|
||||||
|
const linkId = request.qs()?.linkId;
|
||||||
|
if (!linkId) {
|
||||||
|
return this.collectionsService.redirectToDashboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
const link = await this.linkService.getLinkById(linkId, auth.user!.id);
|
||||||
|
await link.load('collection');
|
||||||
|
return inertia.render('links/delete', { link });
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute({ request, auth }: HttpContext) {
|
||||||
|
const { params } = await request.validateUsing(deleteLinkValidator);
|
||||||
|
|
||||||
|
const link = await this.linkService.getLinkById(params.id, auth.user!.id);
|
||||||
|
await this.linkService.deleteLink(params.id);
|
||||||
|
|
||||||
|
return this.collectionsService.redirectToCollectionId(link.collectionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTotalLinksCount() {
|
||||||
|
const totalCount = await db.from('links').count('* as total');
|
||||||
|
return Number(totalCount[0].total);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
import CollectionsController from '#collections/controllers/show_collections_controller';
|
|
||||||
import Link from '#links/models/link';
|
|
||||||
import { createLinkValidator } from '#links/validators/create_link_validator';
|
|
||||||
import { deleteLinkValidator } from '#links/validators/delete_link_validator';
|
|
||||||
import { updateLinkFavoriteStatusValidator } from '#links/validators/update_favorite_link_validator';
|
|
||||||
import { updateLinkValidator } from '#links/validators/update_link_validator';
|
|
||||||
import { inject } from '@adonisjs/core';
|
|
||||||
import { HttpContext } from '@adonisjs/core/http';
|
|
||||||
import db from '@adonisjs/lucid/services/db';
|
|
||||||
|
|
||||||
@inject()
|
|
||||||
export default class LinksController {
|
|
||||||
constructor(protected collectionsController: CollectionsController) {}
|
|
||||||
|
|
||||||
async showCreatePage({ auth, inertia }: HttpContext) {
|
|
||||||
const collections =
|
|
||||||
await this.collectionsController.getCollectionsByAuthorId(auth.user!.id);
|
|
||||||
return inertia.render('links/create', { collections });
|
|
||||||
}
|
|
||||||
|
|
||||||
async store({ auth, request, response }: HttpContext) {
|
|
||||||
const { collectionId, ...payload } =
|
|
||||||
await request.validateUsing(createLinkValidator);
|
|
||||||
|
|
||||||
await this.collectionsController.getCollectionById(
|
|
||||||
collectionId,
|
|
||||||
auth.user!.id
|
|
||||||
);
|
|
||||||
await Link.create({
|
|
||||||
...payload,
|
|
||||||
collectionId,
|
|
||||||
authorId: auth.user?.id!,
|
|
||||||
});
|
|
||||||
return this.collectionsController.redirectToCollectionId(
|
|
||||||
response,
|
|
||||||
collectionId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async showEditPage({ auth, inertia, request, response }: HttpContext) {
|
|
||||||
const linkId = request.qs()?.linkId;
|
|
||||||
if (!linkId) {
|
|
||||||
return response.redirectToNamedRoute('dashboard');
|
|
||||||
}
|
|
||||||
|
|
||||||
const userId = auth.user!.id;
|
|
||||||
const collections =
|
|
||||||
await this.collectionsController.getCollectionsByAuthorId(userId);
|
|
||||||
const link = await this.getLinkById(linkId, userId);
|
|
||||||
|
|
||||||
return inertia.render('links/edit', { collections, link });
|
|
||||||
}
|
|
||||||
|
|
||||||
async update({ request, auth, response }: HttpContext) {
|
|
||||||
const { params, ...payload } =
|
|
||||||
await request.validateUsing(updateLinkValidator);
|
|
||||||
|
|
||||||
// Throw if invalid link id provided
|
|
||||||
await this.getLinkById(params.id, auth.user!.id);
|
|
||||||
|
|
||||||
await Link.updateOrCreate(
|
|
||||||
{
|
|
||||||
id: params.id,
|
|
||||||
},
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
|
|
||||||
return response.redirectToNamedRoute('dashboard', {
|
|
||||||
qs: { collectionId: payload.collectionId },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async toggleFavorite({ request, auth, response }: HttpContext) {
|
|
||||||
const { params, favorite } = await request.validateUsing(
|
|
||||||
updateLinkFavoriteStatusValidator
|
|
||||||
);
|
|
||||||
|
|
||||||
// Throw if invalid link id provided
|
|
||||||
await this.getLinkById(params.id, auth.user!.id);
|
|
||||||
|
|
||||||
await Link.updateOrCreate(
|
|
||||||
{
|
|
||||||
id: params.id,
|
|
||||||
},
|
|
||||||
{ favorite }
|
|
||||||
);
|
|
||||||
|
|
||||||
return response.json({ status: 'ok' });
|
|
||||||
}
|
|
||||||
|
|
||||||
async showDeletePage({ auth, inertia, request, response }: HttpContext) {
|
|
||||||
const linkId = request.qs()?.linkId;
|
|
||||||
if (!linkId) {
|
|
||||||
return response.redirectToNamedRoute('dashboard');
|
|
||||||
}
|
|
||||||
|
|
||||||
const link = await this.getLinkById(linkId, auth.user!.id);
|
|
||||||
await link.load('collection');
|
|
||||||
return inertia.render('links/delete', { link });
|
|
||||||
}
|
|
||||||
|
|
||||||
async delete({ request, auth, response }: HttpContext) {
|
|
||||||
const { params } = await request.validateUsing(deleteLinkValidator);
|
|
||||||
|
|
||||||
const link = await this.getLinkById(params.id, auth.user!.id);
|
|
||||||
await link.delete();
|
|
||||||
|
|
||||||
return response.redirectToNamedRoute('dashboard', {
|
|
||||||
qs: { collectionId: link.id },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getTotalLinksCount() {
|
|
||||||
const totalCount = await db.from('links').count('* as total');
|
|
||||||
return Number(totalCount[0].total);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get link by id.
|
|
||||||
*
|
|
||||||
* /!\ Only return private link (create by the current user)
|
|
||||||
*/
|
|
||||||
private async getLinkById(id: Link['id'], userId: Link['id']) {
|
|
||||||
return await Link.query()
|
|
||||||
.where('id', id)
|
|
||||||
.andWhere('author_id', userId)
|
|
||||||
.firstOrFail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
19
app/links/controllers/toggle_favorite_controller.ts
Normal file
19
app/links/controllers/toggle_favorite_controller.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { LinkService } from '#links/services/link_service';
|
||||||
|
import { updateLinkFavoriteStatusValidator } from '#links/validators/update_favorite_link_validator';
|
||||||
|
import { inject } from '@adonisjs/core';
|
||||||
|
import { HttpContext } from '@adonisjs/core/http';
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
export default class ToggleFavoriteController {
|
||||||
|
constructor(private linkService: LinkService) {}
|
||||||
|
|
||||||
|
async toggleFavorite({ request, response }: HttpContext) {
|
||||||
|
const { params, favorite } = await request.validateUsing(
|
||||||
|
updateLinkFavoriteStatusValidator
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.linkService.updateFavorite(params.id, favorite);
|
||||||
|
|
||||||
|
return response.json({ status: 'ok' });
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/links/controllers/update_link_controller.ts
Normal file
34
app/links/controllers/update_link_controller.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { CollectionService } from '#collections/services/collection_service';
|
||||||
|
import { LinkService } from '#links/services/link_service';
|
||||||
|
import { updateLinkValidator } from '#links/validators/update_link_validator';
|
||||||
|
import { inject } from '@adonisjs/core';
|
||||||
|
import { HttpContext } from '@adonisjs/core/http';
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
export default class UpdateLinkController {
|
||||||
|
constructor(
|
||||||
|
private linkService: LinkService,
|
||||||
|
private collectionsService: CollectionService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async render({ auth, inertia, request, response }: HttpContext) {
|
||||||
|
const linkId = request.qs()?.linkId;
|
||||||
|
if (!linkId) {
|
||||||
|
return response.redirectToNamedRoute('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
const collections =
|
||||||
|
await this.collectionsService.getCollectionsForAuthenticatedUser();
|
||||||
|
const link = await this.linkService.getLinkById(linkId, auth.user!.id);
|
||||||
|
|
||||||
|
return inertia.render('links/edit', { collections, link });
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute({ request }: HttpContext) {
|
||||||
|
const { params, ...payload } =
|
||||||
|
await request.validateUsing(updateLinkValidator);
|
||||||
|
|
||||||
|
await this.linkService.updateLink(params.id, payload);
|
||||||
|
return this.collectionsService.redirectToCollectionId(payload.collectionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
import { middleware } from '#start/kernel';
|
import { middleware } from '#start/kernel';
|
||||||
import router from '@adonisjs/core/services/router';
|
import router from '@adonisjs/core/services/router';
|
||||||
const LinksController = () => import('#links/controllers/links_controller');
|
|
||||||
|
const CreateLinkController = () =>
|
||||||
|
import('#links/controllers/create_link_controller');
|
||||||
|
const DeleteLinkController = () =>
|
||||||
|
import('#links/controllers/delete_link_controller');
|
||||||
|
const UpdateLinkController = () =>
|
||||||
|
import('#links/controllers/update_link_controller');
|
||||||
|
const ToggleFavoriteController = () =>
|
||||||
|
import('#links/controllers/toggle_favorite_controller');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Routes for authenticated users
|
* Routes for authenticated users
|
||||||
@@ -8,21 +16,21 @@ const LinksController = () => import('#links/controllers/links_controller');
|
|||||||
router
|
router
|
||||||
.group(() => {
|
.group(() => {
|
||||||
router
|
router
|
||||||
.get('/create', [LinksController, 'showCreatePage'])
|
.get('/create', [CreateLinkController, 'render'])
|
||||||
.as('link.create-form');
|
.as('link.create-form');
|
||||||
router.post('/', [LinksController, 'store']).as('link.create');
|
router.post('/', [CreateLinkController, 'execute']).as('link.create');
|
||||||
|
|
||||||
router.get('/edit', [LinksController, 'showEditPage']).as('link.edit-form');
|
router.get('/edit', [UpdateLinkController, 'render']).as('link.edit-form');
|
||||||
router.put('/:id', [LinksController, 'update']).as('link.edit');
|
router.put('/:id', [UpdateLinkController, 'execute']).as('link.edit');
|
||||||
|
|
||||||
router
|
router
|
||||||
.put('/:id/favorite', [LinksController, 'toggleFavorite'])
|
.put('/:id/favorite', [ToggleFavoriteController, 'toggleFavorite'])
|
||||||
.as('link.toggle-favorite');
|
.as('link.toggle-favorite');
|
||||||
|
|
||||||
router
|
router
|
||||||
.get('/delete', [LinksController, 'showDeletePage'])
|
.get('/delete', [DeleteLinkController, 'render'])
|
||||||
.as('link.delete-form');
|
.as('link.delete-form');
|
||||||
router.delete('/:id', [LinksController, 'delete']).as('link.delete');
|
router.delete('/:id', [DeleteLinkController, 'execute']).as('link.delete');
|
||||||
})
|
})
|
||||||
.middleware([middleware.auth()])
|
.middleware([middleware.auth()])
|
||||||
.prefix('/links');
|
.prefix('/links');
|
||||||
|
|||||||
60
app/links/services/link_service.ts
Normal file
60
app/links/services/link_service.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import Link from '#links/models/link';
|
||||||
|
import { HttpContext } from '@adonisjs/core/http';
|
||||||
|
|
||||||
|
type CreateLinkPayload = {
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
url: string;
|
||||||
|
favorite: boolean;
|
||||||
|
collectionId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UpdateLinkPayload = CreateLinkPayload;
|
||||||
|
|
||||||
|
export class LinkService {
|
||||||
|
createLink(payload: CreateLinkPayload) {
|
||||||
|
const context = this.getAuthContext();
|
||||||
|
return Link.create({
|
||||||
|
...payload,
|
||||||
|
authorId: context.auth.user!.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLink(id: number, payload: UpdateLinkPayload) {
|
||||||
|
const context = this.getAuthContext();
|
||||||
|
return Link.query()
|
||||||
|
.where('id', id)
|
||||||
|
.andWhere('author_id', context.auth.user!.id)
|
||||||
|
.update(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteLink(id: number) {
|
||||||
|
const context = this.getAuthContext();
|
||||||
|
return Link.query()
|
||||||
|
.where('id', id)
|
||||||
|
.andWhere('author_id', context.auth.user!.id)
|
||||||
|
.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLinkById(id: Link['id'], userId: Link['id']) {
|
||||||
|
return await Link.query()
|
||||||
|
.where('id', id)
|
||||||
|
.andWhere('author_id', userId)
|
||||||
|
.firstOrFail();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFavorite(id: number, favorite: boolean) {
|
||||||
|
return Link.query()
|
||||||
|
.where('id', id)
|
||||||
|
.andWhere('author_id', this.getAuthContext().auth.user!.id)
|
||||||
|
.update({ favorite });
|
||||||
|
}
|
||||||
|
|
||||||
|
getAuthContext() {
|
||||||
|
const context = HttpContext.getOrFail();
|
||||||
|
if (!context.auth.user || !context.auth.user.id) {
|
||||||
|
throw new Error('User not authenticated');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user