fix+feat(everything): lot of things
Some checks failed
Deploy to Development / build-and-deploy (push) Failing after 20s
Some checks failed
Deploy to Development / build-and-deploy (push) Failing after 20s
This commit is contained in:
@@ -1,15 +1,67 @@
|
||||
FROM node:18-alpine
|
||||
# Stage 1 : Build prod
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files first for better caching
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci
|
||||
|
||||
# Copy application files
|
||||
COPY . .
|
||||
|
||||
# Run the application in development mode
|
||||
CMD ["npm", "run", "dev"]
|
||||
ARG VITE_API_URL=https://api.lediscord.com
|
||||
ARG VITE_APP_URL=https://lediscord.com
|
||||
ARG VITE_UPLOAD_URL=https://api.lediscord.com/uploads
|
||||
|
||||
ENV VITE_API_URL=$VITE_API_URL
|
||||
ENV VITE_APP_URL=$VITE_APP_URL
|
||||
ENV VITE_UPLOAD_URL=$VITE_UPLOAD_URL
|
||||
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2 : Image finale avec les deux modes
|
||||
FROM node:20-alpine
|
||||
|
||||
RUN apk add --no-cache nginx
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copier les sources pour le mode dev
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
|
||||
# Copier le build prod
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# Config nginx
|
||||
RUN echo 'server { \
|
||||
listen 8080; \
|
||||
root /usr/share/nginx/html; \
|
||||
index index.html; \
|
||||
location / { \
|
||||
try_files $uri $uri/ /index.html; \
|
||||
} \
|
||||
location /assets { \
|
||||
expires 1y; \
|
||||
add_header Cache-Control "public, immutable"; \
|
||||
} \
|
||||
}' > /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Script d'entrée
|
||||
COPY <<EOF /entrypoint.sh
|
||||
#!/bin/sh
|
||||
if [ "\$MODE" = "dev" ]; then
|
||||
echo "🔧 Mode DEVELOPPEMENT"
|
||||
exec npm run dev -- --host 0.0.0.0 --port 8080
|
||||
else
|
||||
echo "🚀 Mode PRODUCTION"
|
||||
exec nginx -g "daemon off;"
|
||||
fi
|
||||
EOF
|
||||
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
CMD ["/entrypoint.sh"]
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="video-player-container">
|
||||
<div class="video-player-container px-2 sm:px-0">
|
||||
<div class="relative">
|
||||
<!-- Video.js Player -->
|
||||
<div data-vjs-player>
|
||||
|
||||
@@ -71,7 +71,14 @@
|
||||
</div>
|
||||
|
||||
<!-- Participation Badge -->
|
||||
<div class="absolute top-2 right-2">
|
||||
<div class="absolute top-2 right-2 flex gap-1">
|
||||
<span
|
||||
v-if="event.is_private"
|
||||
class="px-2 py-1 rounded-full text-xs font-medium text-white bg-purple-600"
|
||||
title="Événement privé"
|
||||
>
|
||||
🔒 Privé
|
||||
</span>
|
||||
<span
|
||||
v-if="getUserParticipation(event)"
|
||||
class="px-2 py-1 rounded-full text-xs font-medium text-white"
|
||||
@@ -235,6 +242,68 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Type d'événement -->
|
||||
<div>
|
||||
<label class="label">Type d'événement</label>
|
||||
<div class="flex gap-4">
|
||||
<label class="flex items-center cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
v-model="newEvent.is_private"
|
||||
:value="false"
|
||||
class="mr-2"
|
||||
>
|
||||
<span class="text-sm">Public (visible par tous)</span>
|
||||
</label>
|
||||
<label class="flex items-center cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
v-model="newEvent.is_private"
|
||||
:value="true"
|
||||
class="mr-2"
|
||||
>
|
||||
<span class="text-sm">Privé (invitations uniquement)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sélection des invités pour événements privés -->
|
||||
<div v-if="newEvent.is_private">
|
||||
<label class="label">Inviter des membres</label>
|
||||
<div class="border border-gray-300 rounded-lg p-3 max-h-48 overflow-y-auto">
|
||||
<div
|
||||
v-for="user in users.filter(u => u.id !== authStore.user?.id)"
|
||||
:key="user.id"
|
||||
class="flex items-center justify-between py-2 border-b border-gray-100 last:border-b-0"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
v-if="user.avatar_url"
|
||||
:src="getMediaUrl(user.avatar_url)"
|
||||
:alt="user.full_name"
|
||||
class="w-8 h-8 rounded-full mr-2 object-cover"
|
||||
>
|
||||
<div v-else class="w-8 h-8 rounded-full bg-primary-100 flex items-center justify-center mr-2">
|
||||
<User class="w-4 h-4 text-primary-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm font-medium text-gray-900">{{ user.full_name }}</div>
|
||||
<div class="text-xs text-gray-500">@{{ user.username }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
:value="user.id"
|
||||
v-model="newEvent.invited_user_ids"
|
||||
class="w-4 h-4 text-primary-600 rounded focus:ring-primary-500"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mt-2">
|
||||
Sélectionnez les membres à inviter à cet événement privé
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button
|
||||
type="button"
|
||||
@@ -294,7 +363,9 @@ const newEvent = ref({
|
||||
description: '',
|
||||
date: '',
|
||||
location: '',
|
||||
end_date: null
|
||||
end_date: null,
|
||||
is_private: false,
|
||||
invited_user_ids: []
|
||||
})
|
||||
|
||||
const eventMentions = ref([])
|
||||
@@ -399,6 +470,12 @@ async function quickParticipation(eventId, status) {
|
||||
async function createEvent() {
|
||||
if (!newEvent.value.title || !newEvent.value.date) return
|
||||
|
||||
// Vérifier que des invités sont sélectionnés pour les événements privés
|
||||
if (newEvent.value.is_private && (!newEvent.value.invited_user_ids || newEvent.value.invited_user_ids.length === 0)) {
|
||||
toast.warning('Veuillez sélectionner au moins un membre à inviter pour un événement privé')
|
||||
return
|
||||
}
|
||||
|
||||
creating.value = true
|
||||
try {
|
||||
const eventData = {
|
||||
@@ -406,7 +483,9 @@ async function createEvent() {
|
||||
description: newEvent.value.description,
|
||||
date: new Date(newEvent.value.date).toISOString(),
|
||||
location: newEvent.value.location,
|
||||
end_date: newEvent.value.end_date ? new Date(newEvent.value.end_date).toISOString() : null
|
||||
end_date: newEvent.value.end_date ? new Date(newEvent.value.end_date).toISOString() : null,
|
||||
is_private: newEvent.value.is_private,
|
||||
invited_user_ids: newEvent.value.is_private ? newEvent.value.invited_user_ids : null
|
||||
}
|
||||
|
||||
await axios.post('/api/events', eventData)
|
||||
@@ -416,7 +495,7 @@ async function createEvent() {
|
||||
|
||||
showCreateModal.value = false
|
||||
resetForm()
|
||||
toast.success('Événement créé avec succès')
|
||||
toast.success(newEvent.value.is_private ? 'Événement privé créé avec succès' : 'Événement créé avec succès')
|
||||
} catch (error) {
|
||||
toast.error('Erreur lors de la création de l\'événement')
|
||||
}
|
||||
@@ -429,7 +508,9 @@ function resetForm() {
|
||||
description: '',
|
||||
date: '',
|
||||
location: '',
|
||||
end_date: null
|
||||
end_date: null,
|
||||
is_private: false,
|
||||
invited_user_ids: []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user