Skip to content

Commit b5f455e

Browse files
authored
Merge pull request #4 from javascriptcm/feat/disable-cors
desactivation des cors
2 parents c94f5ea + 0c03265 commit b5f455e

65 files changed

Lines changed: 6176 additions & 227 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ yarn-error.log
2323

2424
# Platform specific
2525
.DS_Store
26-
memory-bank/
26+
memory-bank/
27+
.claude/
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { HttpContext } from '@adonisjs/core/http'
2+
import MinioService from '#services/minio_service'
3+
4+
export default class UploadController {
5+
async presign({ request }: HttpContext) {
6+
const { fileName, mimeType } = request.only(['fileName', 'mimeType'])
7+
const { url, key } = await MinioService.getPresignedUrl(fileName, mimeType)
8+
return { url, key }
9+
}
10+
11+
async presignView({ request }: HttpContext) {
12+
const key = request.input('key') || request.qs().key
13+
if (!key) {
14+
return { error: 'Missing key' }
15+
}
16+
const url = await MinioService.getPresignedViewUrl(key)
17+
return { url }
18+
}
19+
}
Lines changed: 218 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
import { HttpContext } from '@adonisjs/core/http'
2-
import { articleValidator } from '#validators/article_validator'
1+
import { ArticleStatus } from '#enums/article_status'
2+
import { Role } from '#enums/role'
33
import Article from '#models/article'
4+
import ArticleStatsService from '#services/article_stats_service'
5+
import { articleValidator } from '#validators/article_validator'
6+
import { HttpContext } from '@adonisjs/core/http'
47
import { DateTime } from 'luxon'
58

69
export default class ArticlesController {
710
async index({ inertia, request }: HttpContext) {
811
const page = request.input('page', 1)
912
const articles = await Article.query()
1013
.preload('author')
11-
.where('is_published', true)
14+
.where('status', ArticleStatus.PUBLISHED)
1215
.orderBy('published_at', 'desc')
1316
.paginate(page, 10)
1417

@@ -23,19 +26,20 @@ export default class ArticlesController {
2326

2427
async store({ request, auth, response }: HttpContext) {
2528
const data = await articleValidator.validate(request.all())
26-
const article = new Article()
27-
28-
article.title = data.title
29-
article.content = data.content
30-
article.excerpt = data.excerpt
31-
article.isPublished = data.isPublished || false
32-
article.authorId = auth.user!.id
33-
if (data.isPublished) {
34-
article.publishedAt = DateTime.now()
29+
30+
const payload: Partial<Article> = { ...data, authorId: auth.user!.id }
31+
32+
if (data.status === ArticleStatus.PUBLISHED) {
33+
payload.publishedAt = DateTime.now()
3534
}
3635

37-
await article.generateSlug()
38-
await article.save()
36+
payload.slug = Article.generateSlug(data.title)
37+
38+
console.log('payload', payload)
39+
40+
const article = await Article.create(payload)
41+
42+
console.log('article', article)
3943

4044
return response.redirect().toRoute('articles.show', { slug: article.slug })
4145
}
@@ -45,4 +49,204 @@ export default class ArticlesController {
4549

4650
return inertia.render('articles/[slug]', { article })
4751
}
52+
53+
async dashboard({ inertia, auth }: HttpContext) {
54+
const isAdmin = auth.user!.role === Role.ADMIN
55+
const stats = await ArticleStatsService.getStats(auth.user!.id, isAdmin)
56+
return inertia.render('dashboard/index', {
57+
publishedArticles: stats.published,
58+
draftArticles: stats.drafts,
59+
bannedArticles: stats.banned,
60+
discussions: 0,
61+
questions: 0,
62+
})
63+
}
64+
65+
async articles({ inertia, request, auth }: HttpContext) {
66+
const page = request.input('page', 1)
67+
const status = request.input('status', null)
68+
const isAdmin = auth.user!.role === Role.ADMIN
69+
70+
let query = Article.query().preload('author').orderBy('created_at', 'desc')
71+
72+
// Logique différente selon le rôle
73+
if (isAdmin) {
74+
// Admin : voir tous les articles publiés/banned + ses propres brouillons
75+
if (status && Object.values(ArticleStatus).includes(status as ArticleStatus)) {
76+
if (status === ArticleStatus.PUBLISHED) {
77+
// Voir tous les articles publiés (de tous les auteurs)
78+
query.where('status', ArticleStatus.PUBLISHED)
79+
} else if (status === ArticleStatus.BANNED) {
80+
// Voir tous les articles banned (de tous les auteurs)
81+
query.where('status', ArticleStatus.BANNED)
82+
} else {
83+
// Voir uniquement ses propres brouillons
84+
query.where('status', status as ArticleStatus).where('author_id', auth.user!.id)
85+
}
86+
} else {
87+
// Par défaut : tous les publiés + ses brouillons
88+
query.where((builder) => {
89+
builder
90+
.where('status', ArticleStatus.PUBLISHED)
91+
.orWhere((subBuilder) => {
92+
subBuilder.where('author_id', auth.user!.id).where('status', ArticleStatus.DRAFT)
93+
})
94+
})
95+
}
96+
} else {
97+
// Membre : voir uniquement ses propres articles
98+
query.where('author_id', auth.user!.id)
99+
100+
if (status && Object.values(ArticleStatus).includes(status as ArticleStatus)) {
101+
query.where('status', status as ArticleStatus)
102+
}
103+
}
104+
105+
const articles = await query.paginate(page, 10)
106+
107+
return inertia.render('dashboard/articles', {
108+
articles: articles.toJSON(),
109+
currentStatus: status,
110+
isAdmin,
111+
})
112+
}
113+
114+
async edit({ inertia, params, auth, response }: HttpContext) {
115+
const article = await Article.query()
116+
.where('slug', params.slug)
117+
.where('author_id', auth.user!.id)
118+
.firstOrFail()
119+
120+
return inertia.render('dashboard/articles/edit', { article })
121+
}
122+
123+
async update({ request, params, auth, response }: HttpContext) {
124+
const article = await Article.query()
125+
.where('slug', params.slug)
126+
.where('author_id', auth.user!.id)
127+
.firstOrFail()
128+
129+
const data = await articleValidator.validate(request.all())
130+
131+
const payload: Partial<Article> = { ...data }
132+
133+
// Si on passe de draft/waiting à published, mettre à jour publishedAt
134+
if (
135+
data.status === ArticleStatus.PUBLISHED &&
136+
article.status !== ArticleStatus.PUBLISHED
137+
) {
138+
payload.publishedAt = DateTime.now()
139+
}
140+
141+
// Si le titre change, générer un nouveau slug
142+
if (data.title !== article.title) {
143+
payload.slug = Article.generateSlug(data.title)
144+
}
145+
146+
await article.merge(payload).save()
147+
148+
return response.redirect().toRoute('articles.show', { slug: article.slug })
149+
}
150+
151+
// ADMIN METHODS
152+
153+
/**
154+
* Liste des articles pour l'admin
155+
* - Tous les articles publiés (de tous les auteurs)
156+
* - Ses propres brouillons seulement
157+
*/
158+
async adminArticles({ inertia, request, auth }: HttpContext) {
159+
const page = request.input('page', 1)
160+
const status = request.input('status', null)
161+
162+
let query = Article.query().preload('author').orderBy('created_at', 'desc')
163+
164+
if (status && Object.values(ArticleStatus).includes(status as ArticleStatus)) {
165+
if (status === ArticleStatus.PUBLISHED) {
166+
// Admin voit tous les articles publiés
167+
query.where('status', ArticleStatus.PUBLISHED)
168+
} else {
169+
// Admin ne voit que ses propres brouillons/en attente
170+
query.where('status', status as ArticleStatus).where('author_id', auth.user!.id)
171+
}
172+
} else {
173+
// Par défaut : tous les publiés + ses brouillons
174+
query.where((builder) => {
175+
builder
176+
.where('status', ArticleStatus.PUBLISHED)
177+
.orWhere((subBuilder) => {
178+
subBuilder.where('author_id', auth.user!.id).whereIn('status', [
179+
ArticleStatus.DRAFT,
180+
ArticleStatus.WAITING_APPROVAL,
181+
])
182+
})
183+
})
184+
}
185+
186+
const articles = await query.paginate(page, 10)
187+
188+
return inertia.render('admin/articles', {
189+
articles: articles.toJSON(),
190+
currentStatus: status,
191+
})
192+
}
193+
194+
/**
195+
* Dépublier un article (admin seulement)
196+
* Passe le statut de PUBLISHED à DRAFT
197+
*/
198+
async unpublish({ params, response, session, auth }: HttpContext) {
199+
const article = await Article.query().where('slug', params.slug).firstOrFail()
200+
201+
// Vérifier que l'article est publié
202+
if (article.status !== ArticleStatus.PUBLISHED) {
203+
session.flash('error', 'Cet article n\'est pas publié')
204+
return response.redirect().back()
205+
}
206+
207+
// Dépublier l'article
208+
article.status = ArticleStatus.DRAFT
209+
article.publishedAt = null
210+
await article.save()
211+
212+
session.flash('success', 'Article dépublié avec succès')
213+
return response.redirect().back()
214+
}
215+
216+
/**
217+
* Bannir un article (admin seulement)
218+
*/
219+
async ban({ params, request, response, session }: HttpContext) {
220+
const article = await Article.query().where('slug', params.slug).firstOrFail()
221+
const banReason = request.input('ban_reason')
222+
223+
article.status = ArticleStatus.BANNED
224+
article.banReason = banReason
225+
article.publishedAt = null
226+
await article.save()
227+
228+
session.flash('success', 'Article banni avec succès')
229+
return response.redirect().back()
230+
}
231+
232+
/**
233+
* Débannir un article (admin seulement)
234+
*/
235+
async unban({ params, response, session }: HttpContext) {
236+
const article = await Article.query().where('slug', params.slug).firstOrFail()
237+
238+
// Vérifier que l'article est banni
239+
if (article.status !== ArticleStatus.BANNED) {
240+
session.flash('error', 'Cet article n\'est pas banni')
241+
return response.redirect().back()
242+
}
243+
244+
// Débannir l'article (le repasser en brouillon)
245+
article.status = ArticleStatus.DRAFT
246+
article.banReason = null
247+
await article.save()
248+
249+
session.flash('success', 'Article débanni avec succès')
250+
return response.redirect().back()
251+
}
48252
}

app/controllers/auth/register_controller.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { HttpContext } from '@adonisjs/core/http'
22
import User from '#models/user'
33
import { registerValidator } from '#validators/register_validator'
4+
import { Role } from '#enums/role'
45

56
export default class RegisterController {
67
async show({ inertia }: HttpContext) {
@@ -10,7 +11,10 @@ export default class RegisterController {
1011
async store({ request, auth, response }: HttpContext) {
1112
const data = await registerValidator.validate(request.all())
1213

13-
const user = await User.create(data)
14+
const user = await User.create({
15+
...data,
16+
role: (data.role as Role) || Role.MEMBER,
17+
})
1418
await auth.use('web').login(user)
1519

1620
return response.redirect().toRoute('dashboard')

0 commit comments

Comments
 (0)