Compare commits
8 Commits
a5fe876e3f
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19b83e9ac1 | ||
| 9705257178 | |||
| 2f78442deb | |||
|
|
c586cc3932 | ||
|
|
11694d36dd | ||
|
|
3b05938162 | ||
|
|
f75a1481bd | ||
|
|
bb791e35d1 |
35
.github/workflows/deploy.yml
vendored
35
.github/workflows/deploy.yml
vendored
@@ -1,35 +0,0 @@
|
|||||||
name: Deploy App
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
container:
|
|
||||||
volumes:
|
|
||||||
- /var/docker-web:/var/docker-web
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Prepare and build app
|
|
||||||
run: |
|
|
||||||
REPO_NAME="${GITHUB_REPOSITORY##*/}"
|
|
||||||
APP_DIR="/var/docker-web/apps/${REPO_NAME}"
|
|
||||||
bash /var/docker-web/src/cli.sh down "${REPO_NAME}"
|
|
||||||
rm -rf "$APP_DIR"
|
|
||||||
mkdir "$APP_DIR"
|
|
||||||
cp -a $(find . -mindepth 1 -maxdepth 1 ! -name '.git' ! -name 'node_modules') "$APP_DIR/"
|
|
||||||
export COMPOSE_BAKE=false
|
|
||||||
docker rmi "local/${REPO_NAME}" 2>/dev/null || true
|
|
||||||
bash /var/docker-web/src/cli.sh build "${REPO_NAME}"
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
needs: build
|
|
||||||
container:
|
|
||||||
volumes:
|
|
||||||
- /var/docker-web:/var/docker-web
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Deploy with docker-web
|
|
||||||
run: |
|
|
||||||
REPO_NAME="${GITHUB_REPOSITORY##*/}"
|
|
||||||
bash /var/docker-web/src/cli.sh up "${REPO_NAME}"
|
|
||||||
23
.github/workflows/merge.yml
vendored
Normal file
23
.github/workflows/merge.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: Delete Merged Branches
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [closed]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
delete-branch:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
container:
|
||||||
|
volumes:
|
||||||
|
- /var/docker-web:/var/docker-web
|
||||||
|
if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Delete merged branch
|
||||||
|
run: |
|
||||||
|
BRANCH_NAME="${{ github.event.pull_request.head.ref || github.event.inputs.branch_name }}"
|
||||||
|
APP_NAME="${BRANCH_NAME}_${GITHUB_REPOSITORY##*/}"
|
||||||
|
if [ "$BRANCH_NAME" != "main" ] && [ "$BRANCH_NAME" != "develop" ] && [ -n "$APP_NAME" ]; then
|
||||||
|
bash /var/docker-web/src/cli.sh rm -y "${APP_NAME}"
|
||||||
|
else
|
||||||
|
echo "Cannot delete protected branch: $BRANCH_NAME"
|
||||||
|
fi
|
||||||
41
.github/workflows/push.yml
vendored
Normal file
41
.github/workflows/push.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: Deploy App
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
container:
|
||||||
|
volumes:
|
||||||
|
- /var/docker-web:/var/docker-web
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Build app
|
||||||
|
run: |
|
||||||
|
bash ./.github/workflows/setup-env.sh
|
||||||
|
set -a && source .env && set +a
|
||||||
|
if [ -n "$APP_NAME" ]; then
|
||||||
|
set -a && source .env && set +a
|
||||||
|
bash /var/docker-web/src/cli.sh down "${APP_NAME}"
|
||||||
|
rm -rf "$APP_DIR"
|
||||||
|
mkdir "$APP_DIR"
|
||||||
|
cp -a $(find . -mindepth 1 -maxdepth 1 ! -name '.git' ! -name 'node_modules') "$APP_DIR/"
|
||||||
|
export COMPOSE_BAKE=false
|
||||||
|
docker rmi "local/${APP_NAME}" 2>/dev/null || true
|
||||||
|
bash /var/docker-web/src/cli.sh build "${APP_NAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
needs: build
|
||||||
|
container:
|
||||||
|
volumes:
|
||||||
|
- /var/docker-web:/var/docker-web
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Deploy
|
||||||
|
run: |
|
||||||
|
bash ./.github/workflows/setup-env.sh
|
||||||
|
set -a && source .env && set +a
|
||||||
|
if [ -n "$APP_NAME" ]; then
|
||||||
|
bash /var/docker-web/src/cli.sh up "${APP_NAME}"
|
||||||
|
fi
|
||||||
34
.github/workflows/setup-env.sh
vendored
Normal file
34
.github/workflows/setup-env.sh
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -a && source .env && set +a
|
||||||
|
|
||||||
|
changeEnvVar() {
|
||||||
|
local var_name="$1"
|
||||||
|
local new_value="$2"
|
||||||
|
local env_file=".env"
|
||||||
|
|
||||||
|
if grep -q "^${var_name}=" "$env_file"; then
|
||||||
|
sed -i "s|${var_name}=.*|${var_name}=${new_value}|" "$env_file"
|
||||||
|
else
|
||||||
|
echo "${var_name}=${new_value}" >> "$env_file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Variables GitHub
|
||||||
|
APP_NAME="${GITHUB_REPOSITORY##*/}"
|
||||||
|
BRANCH_NAME=$GITHUB_REF_NAME
|
||||||
|
|
||||||
|
# Configuration pour les branches non-principales
|
||||||
|
if [ "$BRANCH_NAME" != "main" ] && [ "$BRANCH_NAME" != "master" ]; then
|
||||||
|
DOMAIN="$BRANCH_NAME.$DOMAIN"
|
||||||
|
APP_NAME="${BRANCH_NAME}_${APP_NAME}"
|
||||||
|
PORT=$(bash /var/docker-web/src/cli.sh ALLOCATE_PORT)
|
||||||
|
sed -i "s|${GITHUB_REPOSITORY##*/}|$APP_NAME|g" docker-compose.yml
|
||||||
|
fi
|
||||||
|
|
||||||
|
APP_DIR="/var/docker-web/apps/$APP_NAME"
|
||||||
|
|
||||||
|
changeEnvVar "DOMAIN" $DOMAIN
|
||||||
|
changeEnvVar "APP_NAME" $APP_NAME
|
||||||
|
changeEnvVar "PORT" $PORT
|
||||||
|
changeEnvVar "APP_DIR" $APP_DIR
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
# Builder
|
# Builder
|
||||||
FROM node:20-bookworm AS builder
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm install --legacy-peer-deps
|
RUN npm install --legacy-peer-deps
|
||||||
@@ -8,9 +9,12 @@ COPY . .
|
|||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# Runtime
|
# Runtime
|
||||||
FROM node:20-slim AS runner
|
FROM node:20-alpine AS runner
|
||||||
|
RUN apk add --no-cache python3 make g++ sqlite
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=builder /app/.output ./.output
|
COPY --from=builder /app/.output ./.output
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
COPY ./server/database ./server/database
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
CMD ["node", ".output/server/index.mjs"]
|
CMD ["node", ".output/server/index.mjs"]
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="platine pointer-events-none" :class="{ 'drag-over': isDragOver }" @dragenter.prevent="onDragEnter"
|
<div class="platine pointer-events-none" :class="{ 'loading': platineStore.isLoadingTrack, 'mounted': isMounted }"
|
||||||
@dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop.prevent="onDrop">
|
ref="platine">
|
||||||
<div class="disc pointer-events-auto fixed" ref="discRef" :style="'background-image: url(/card-dock.svg)'"
|
<img class="cover" :src="platineStore.currentTrack?.coverId" />
|
||||||
id="disc">
|
<div class="disc pointer-events-auto fixed bg-transparent" ref="discRef" id="disc">
|
||||||
<div
|
<div class="bobine"
|
||||||
class="bobine bg-slate-900 bg-opacity-50 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 rounded-full"
|
|
||||||
:style="{ height: platineStore.progressPercentage + '%', width: platineStore.progressPercentage + '%' }"></div>
|
:style="{ height: platineStore.progressPercentage + '%', width: platineStore.progressPercentage + '%' }"></div>
|
||||||
<img class="absolute size-full rounded-full" :src="platineStore.currentTrack?.coverId"
|
|
||||||
:alt="platineStore.currentTrack?.title">
|
|
||||||
|
|
||||||
<div class="disc-label rounded-full bg-cover bg-center">
|
<div class="disc-label rounded-full bg-cover bg-center">
|
||||||
<img src="/favicon.svg" class="size-1/3">
|
<img src="/favicon.svg" class="size-1/2 bg-black rounded-full p-5">
|
||||||
<div v-if="platineStore.isLoadingTrack" class="loading-indicator">
|
<div v-if="platineStore.isLoadingTrack" class="loading-indicator">
|
||||||
<div class="spinner"></div>
|
<div class="spinner"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,11 +16,11 @@
|
|||||||
<div v-if="!platineStore.isLoadingTrack" class="absolute top-1/2 right-8 size-1/12 rounded-full bg-esyellow">
|
<div v-if="!platineStore.isLoadingTrack" class="absolute top-1/2 right-8 size-1/12 rounded-full bg-esyellow">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full h-1/5 text-base">
|
<!-- <div class="w-full h-1/5 text-base">
|
||||||
{{ platineStore.currentTrack?.title }}
|
{{ platineStore.currentTrack?.title }}
|
||||||
<br>
|
<br>
|
||||||
{{ platineStore.currentTrack?.artist?.name }}
|
{{ platineStore.currentTrack?.artist?.name }}
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -34,41 +32,12 @@ import type { Track } from '~~/types/types'
|
|||||||
const props = defineProps<{ track?: Track }>()
|
const props = defineProps<{ track?: Track }>()
|
||||||
const platineStore = usePlatineStore()
|
const platineStore = usePlatineStore()
|
||||||
const discRef = ref<HTMLElement>()
|
const discRef = ref<HTMLElement>()
|
||||||
const isDragOver = ref(false)
|
const platine = ref<HTMLElement>()
|
||||||
|
const isMounted = ref(false)
|
||||||
// Gestion du drag and drop
|
|
||||||
const onDragEnter = (e: DragEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
isDragOver.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const onDragOver = (e: DragEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
isDragOver.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const onDragLeave = () => {
|
|
||||||
isDragOver.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
const onDrop = (e: DragEvent) => {
|
|
||||||
isDragOver.value = false
|
|
||||||
const cardData = e.dataTransfer?.getData('application/json')
|
|
||||||
|
|
||||||
if (cardData) {
|
|
||||||
try {
|
|
||||||
const newTrack = JSON.parse(cardData)
|
|
||||||
if (newTrack && newTrack.url) {
|
|
||||||
platineStore.loadTrack(newTrack)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Erreur lors du traitement de la carte déposée', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialisation du lecteur
|
// Initialisation du lecteur
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
isMounted.value = true
|
||||||
if (discRef.value) {
|
if (discRef.value) {
|
||||||
platineStore.initPlatine(discRef.value)
|
platineStore.initPlatine(discRef.value)
|
||||||
}
|
}
|
||||||
@@ -76,6 +45,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
// Nettoyage
|
// Nettoyage
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
isMounted.value = false
|
||||||
platineStore.cleanup()
|
platineStore.cleanup()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -94,21 +64,34 @@ watch(() => props.track, (newTrack) => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
z-index: 99;
|
|
||||||
top: -20%;
|
top: -20%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
transform: translate(-50%, 50%);
|
transform: translate(-50%, 50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cover {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-radius: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transition: opacity 3s ease;
|
||||||
|
|
||||||
|
.loading & {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.disc {
|
.disc {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: white;
|
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -116,10 +99,10 @@ watch(() => props.track, (newTrack) => {
|
|||||||
cursor: grab;
|
cursor: grab;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 0 20px rgba(0, 0, 0, 0.4);
|
||||||
|
|
||||||
.dragoOver & {
|
.loading & {
|
||||||
background-color: #4CAF50;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +175,6 @@ watch(() => props.track, (newTrack) => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
z-index: 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
@@ -211,15 +193,6 @@ watch(() => props.track, (newTrack) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bobine {
|
.bobine {
|
||||||
&::before {
|
@apply bg-slate-900 bg-opacity-50 backdrop-blur absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 rounded-full;
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-size: cover;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<slot />
|
<slot />
|
||||||
<Bucket />
|
<Bucket />
|
||||||
<Platine />
|
<Platine v-if="playerStore.currentTrack" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Track } from '~~/types/types'
|
import type { Track } from '~~/types/types'
|
||||||
|
import { usePlayerStore } from '~/store/player'
|
||||||
|
|
||||||
|
const playerStore = usePlayerStore()
|
||||||
const onCardDropped = (card: Track) => {
|
const onCardDropped = (card: Track) => {
|
||||||
console.log('Carte déposée dans le bucket:', card)
|
console.log('Carte déposée dans le bucket:', card)
|
||||||
}
|
}
|
||||||
@@ -16,16 +18,17 @@ const onCardDropped = (card: Track) => {
|
|||||||
.bucket,
|
.bucket,
|
||||||
.platine {
|
.platine {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: -100%;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bucket {
|
.bucket {
|
||||||
z-index: 70;
|
z-index: 70;
|
||||||
bottom: -260px;
|
bottom: -260px;
|
||||||
transition: bottom 0.3s ease;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
|
transition: bottom .3s ease;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
.card-dragging & {
|
.card-dragging & {
|
||||||
@@ -38,9 +41,15 @@ const onCardDropped = (card: Track) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.platine {
|
.platine {
|
||||||
z-index: 60;
|
bottom: -100%;
|
||||||
bottom: -70%;
|
transition: bottom 2s ease;
|
||||||
transition: bottom 0.3s ease;
|
|
||||||
/* width: 25%; */
|
&.mounted {
|
||||||
|
z-index: 80;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
max-width: 450px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
3
env.sh
3
env.sh
@@ -1,3 +0,0 @@
|
|||||||
export DOMAIN="evilspins.com"
|
|
||||||
export PORT="7901"
|
|
||||||
export PORT_EXPOSED="3000"
|
|
||||||
@@ -58,7 +58,7 @@ export default eventHandler(async (event) => {
|
|||||||
const date = new Date(year, month - 1, day, hour)
|
const date = new Date(year, month - 1, day, hour)
|
||||||
const card = getCardFromDate(date)
|
const card = getCardFromDate(date)
|
||||||
const url = `${urlPrefix}/${encodeURIComponent(file)}`
|
const url = `${urlPrefix}/${encodeURIComponent(file)}`
|
||||||
const coverId = `${urlPrefix}/cover/${encodeURIComponent(file).replace(EXT_RE, '.jpg')}`
|
const coverId = `${urlPrefix}/${encodeURIComponent(file).replace(EXT_RE, '.jpg')}`
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: Number(`${year}${index + 1}`),
|
id: Number(`${year}${index + 1}`),
|
||||||
|
|||||||
Reference in New Issue
Block a user