Compare commits

..

14 Commits

Author SHA1 Message Date
72ec02306e 🚀 자동 배포 재테스트 - 2025-10-01 02:42:48
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
2025-10-01 02:42:48 +09:00
37c3b5af9d 🔧 웹훅 서버를 .cjs 확장자로 변경
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 02:37:33 +09:00
1655006115 🔧 웹훅 서버를 ES 모듈 형식으로 수정
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 02:36:00 +09:00
3cd7563c75 🧪 자동 배포 진단 테스트 - 2025-10-01 02:34:24
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 02:34:24 +09:00
7a9a1fcbe8 로고삭제
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 02:32:54 +09:00
6bd16ff68b 🎉 자동 배포 시스템 완성! - 2025-10-01 02:21:52
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 02:21:52 +09:00
dc41178541 🔧 PM2 설정 파일을 CommonJS 형식으로 수정
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 02:15:50 +09:00
0792e5d2bc 🧪 자동 배포 테스트 - 2025-10-01 02:14:48
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 02:14:48 +09:00
cfd25ea1df 🔧 PM2 설정 파일을 .cjs 확장자로 변경
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 02:07:49 +09:00
15603fad41 🔧 PM2 설정 파일을 ES 모듈 형식으로 수정
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 02:06:41 +09:00
0a22b1bf04 🎨 코드 스타일 개선 및 Node.js 22 호환성 설정
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 02:05:01 +09:00
0029f06774 🔧 Node.js 22 호환성 설정 추가
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 02:03:46 +09:00
8edaa0e517 🧪 자동 배포 테스트 - 2025-10-01 02:00:24
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 02:00:24 +09:00
a4bc12e1c4 🧪 자동 배포 테스트 - 2025-10-01 01:48:33
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
2025-10-01 01:48:33 +09:00
11 changed files with 173 additions and 126 deletions

View File

@@ -1 +1 @@
{"id":"dev","timestamp":1759249355595} {"id":"dev","timestamp":1759251838389}

View File

@@ -1 +1 @@
{"id":"dev","timestamp":1759249355595,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]} {"id":"dev","timestamp":1759251838389,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}

View File

@@ -1,5 +1,5 @@
{ {
"date": "2025-09-30T16:22:39.361Z", "date": "2025-09-30T17:04:01.442Z",
"preset": "nitro-dev", "preset": "nitro-dev",
"framework": { "framework": {
"name": "nuxt", "name": "nuxt",
@@ -11,7 +11,7 @@
"dev": { "dev": {
"pid": 77277, "pid": 77277,
"workerAddress": { "workerAddress": {
"socketPath": "/var/folders/qw/cvgxs3ls7x742rc9zgwbzkh80000gn/T/nitro-worker-77277-1-1-2519.sock" "socketPath": "/var/folders/qw/cvgxs3ls7x742rc9zgwbzkh80000gn/T/nitro-worker-77277-3-3-8923.sock"
} }
} }
} }

4
.nuxt/nuxt.d.ts vendored
View File

@@ -1,9 +1,9 @@
// Generated by nuxi // Generated by nuxi
/// <reference types="@vueuse/nuxt" />
/// <reference types="@nuxtjs/device" /> /// <reference types="@nuxtjs/device" />
/// <reference types="@nuxt/telemetry" />
/// <reference types="@pinia/nuxt" /> /// <reference types="@pinia/nuxt" />
/// <reference types="@nuxt/devtools" /> /// <reference types="@nuxt/devtools" />
/// <reference types="@nuxt/telemetry" />
/// <reference types="@vueuse/nuxt" />
/// <reference types="nuxt" /> /// <reference types="nuxt" />
/// <reference path="types/app-defaults.d.ts" /> /// <reference path="types/app-defaults.d.ts" />
/// <reference path="types/plugins.d.ts" /> /// <reference path="types/plugins.d.ts" />

View File

@@ -33,3 +33,9 @@ npm run dev
```sh ```sh
npm run build npm run build
``` ```
# 🚀 자동 배포 테스트 - #오후
# 🚀 자동 배포 테스트 - 2025-10-01 02:00:14
# 🚀 자동 배포 테스트 - 2025-10-01 02:14:30
# 🎉 자동 배포 시스템 완성! - 2025-10-01 02:21:39
# 🧪 자동 배포 테스트 - 2025-10-01 02:34:11
# 🚀 자동 배포 재테스트 - 2025-10-01 02:42:33

24
ecosystem.config.cjs Normal file
View File

@@ -0,0 +1,24 @@
module.exports = {
apps: [
{
name: "music-admin",
script: ".output/server/index.mjs",
cwd: "/var/www/music/music-admin",
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: "1G",
env: {
NODE_ENV: "production",
PORT: 3000,
DATABASE_URL:
"postgresql://musicuser:Tjqjqhdks$321@localhost:5432/musicdb",
JWT_SECRET: "vTEq9OTvIFs3ZbDaszLRL/ZiAEXziemX1Wh1GIeb+DI=",
},
error_file: "/var/log/music-admin-error.log",
out_file: "/var/log/music-admin-out.log",
log_file: "/var/log/music-admin.log",
time: true,
},
],
};

View File

@@ -12,8 +12,8 @@ module.exports = {
NODE_ENV: "production", NODE_ENV: "production",
PORT: 3000, PORT: 3000,
DATABASE_URL: DATABASE_URL:
"postgresql://username:password@localhost:5432/music_admin?schema=public", "postgresql://musicuser:Tjqjqhdks$321@localhost:5432/musicdb",
JWT_SECRET: "your-jwt-secret-key-here", JWT_SECRET: "vTEq9OTvIFs3ZbDaszLRL/ZiAEXziemX1Wh1GIeb+DI=",
}, },
error_file: "/var/log/music-admin-error.log", error_file: "/var/log/music-admin-error.log",
out_file: "/var/log/music-admin-out.log", out_file: "/var/log/music-admin-out.log",

View File

@@ -1,19 +1,21 @@
import { fileURLToPath } from 'node:url' import { fileURLToPath } from "node:url";
import svgLoader from 'vite-svg-loader' import svgLoader from "vite-svg-loader";
import vuetify from 'vite-plugin-vuetify' import vuetify from "vite-plugin-vuetify";
// https://nuxt.com/docs/api/configuration/nuxt-config // https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({ export default defineNuxtConfig({
app: { app: {
head: { head: {
titleTemplate: '%s - NuxtJS Admin Template', titleTemplate: "%s - NuxtJS Admin Template",
title: 'Sneat', title: "Sneat",
link: [{ link: [
rel: 'icon', {
type: 'image/x-icon', rel: "icon",
href: '/favicon.ico', type: "image/x-icon",
}], href: "/favicon.ico",
},
],
}, },
}, },
@@ -22,29 +24,33 @@ export default defineNuxtConfig({
}, },
css: [ css: [
'@core/scss/template/index.scss', "@core/scss/template/index.scss",
'@styles/styles.scss', "@styles/styles.scss",
'@/plugins/iconify/icons.css', "@/plugins/iconify/icons.css",
'@layouts/styles/index.scss', "@layouts/styles/index.scss",
], ],
components: { components: {
dirs: [{ dirs: [
path: '@/@core/components', {
path: "@/@core/components",
pathPrefix: false, pathPrefix: false,
}, { },
path: '~/components/global', {
path: "~/components/global",
global: true, global: true,
}, { },
path: '~/components', {
path: "~/components",
pathPrefix: false, pathPrefix: false,
}], },
],
}, },
plugins: ['@/plugins/vuetify/index.ts', '@/plugins/iconify/index.ts'], plugins: ["@/plugins/vuetify/index.ts", "@/plugins/iconify/index.ts"],
imports: { imports: {
dirs: ['./@core/utils', './@core/composable/', './plugins/*/composables/*'], dirs: ["./@core/utils", "./@core/composable/", "./plugins/*/composables/*"],
}, },
hooks: {}, hooks: {},
@@ -53,17 +59,20 @@ export default defineNuxtConfig({
typedPages: true, typedPages: true,
}, },
// Node.js 22 호환성 설정
compatibilityDate: "2024-10-01",
typescript: { typescript: {
tsConfig: { tsConfig: {
compilerOptions: { compilerOptions: {
paths: { paths: {
'@/*': ['../*'], "@/*": ["../*"],
'@layouts/*': ['../@layouts/*'], "@layouts/*": ["../@layouts/*"],
'@layouts': ['../@layouts'], "@layouts": ["../@layouts"],
'@core/*': ['../@core/*'], "@core/*": ["../@core/*"],
'@core': ['../@core'], "@core": ["../@core"],
'@images/*': ['../assets/images/*'], "@images/*": ["../assets/images/*"],
'@styles/*': ['../assets/styles/*'], "@styles/*": ["../assets/styles/*"],
}, },
}, },
}, },
@@ -77,21 +86,24 @@ export default defineNuxtConfig({
vue: { vue: {
compilerOptions: { compilerOptions: {
isCustomElement: tag => tag === 'swiper-container' || tag === 'swiper-slide', isCustomElement: (tag) =>
tag === "swiper-container" || tag === "swiper-slide",
}, },
}, },
vite: { vite: {
define: { 'process.env': {} }, define: { "process.env": {} },
resolve: { resolve: {
alias: { alias: {
'@': fileURLToPath(new URL('.', import.meta.url)), "@": fileURLToPath(new URL(".", import.meta.url)),
'@core': fileURLToPath(new URL('./@core', import.meta.url)), "@core": fileURLToPath(new URL("./@core", import.meta.url)),
'@layouts': fileURLToPath(new URL('./@layouts', import.meta.url)), "@layouts": fileURLToPath(new URL("./@layouts", import.meta.url)),
'@images': fileURLToPath(new URL('./assets/images/', import.meta.url)), "@images": fileURLToPath(new URL("./assets/images/", import.meta.url)),
'@styles': fileURLToPath(new URL('./assets/styles/', import.meta.url)), "@styles": fileURLToPath(new URL("./assets/styles/", import.meta.url)),
'@configured-variables': fileURLToPath(new URL('./assets/styles/variables/_template.scss', import.meta.url)), "@configured-variables": fileURLToPath(
new URL("./assets/styles/variables/_template.scss", import.meta.url)
),
}, },
}, },
@@ -100,25 +112,23 @@ export default defineNuxtConfig({
}, },
optimizeDeps: { optimizeDeps: {
exclude: ['vuetify'], exclude: ["vuetify"],
entries: [ entries: ["./**/*.vue"],
'./**/*.vue',
],
}, },
plugins: [ plugins: [
svgLoader(), svgLoader(),
vuetify({ vuetify({
styles: { styles: {
configFile: 'assets/styles/variables/_vuetify.scss', configFile: "assets/styles/variables/_vuetify.scss",
}, },
}), }),
], ],
}, },
build: { build: {
transpile: ['vuetify'], transpile: ["vuetify"],
}, },
modules: ['@vueuse/nuxt', '@nuxtjs/device', '@pinia/nuxt'], modules: ["@vueuse/nuxt", "@nuxtjs/device", "@pinia/nuxt"],
}) });

View File

@@ -1,87 +1,84 @@
<script setup lang="ts"> <script setup lang="ts">
import AuthProvider from '@/views/pages/authentication/AuthProvider.vue' import AuthProvider from "@/views/pages/authentication/AuthProvider.vue";
import logo from '@images/logo.svg?raw' import logo from "@images/logo.svg?raw";
import authV1BottomShape from '@images/svg/auth-v1-bottom-shape.svg?url' import authV1BottomShape from "@images/svg/auth-v1-bottom-shape.svg?url";
import authV1TopShape from '@images/svg/auth-v1-top-shape.svg?url' import authV1TopShape from "@images/svg/auth-v1-top-shape.svg?url";
const form = ref({ const form = ref({
user_id: '', user_id: "",
password: '', password: "",
remember: false, remember: false,
}) });
const isPasswordVisible = ref(false) const isPasswordVisible = ref(false);
const isLoading = ref(false) const isLoading = ref(false);
const errorMessage = ref('') const errorMessage = ref("");
const router = useRouter() const router = useRouter();
// 컴포넌트 마운트 시 테스트 // 컴포넌트 마운트 시 테스트
onMounted(() => { onMounted(() => {});
})
const handleLogin = async () => { const handleLogin = async () => {
if (!form.value.user_id || !form.value.password) { if (!form.value.user_id || !form.value.password) {
errorMessage.value = '아이디와 비밀번호를 입력해주세요.' errorMessage.value = "아이디와 비밀번호를 입력해주세요.";
return return;
} }
isLoading.value = true isLoading.value = true;
errorMessage.value = '' errorMessage.value = "";
try { try {
const response = await $fetch("/api/auth/login", {
const response = await $fetch('/api/auth/login', { method: "POST",
method: 'POST',
body: { body: {
user_id: form.value.user_id, user_id: form.value.user_id,
password: form.value.password password: form.value.password,
} },
}) });
if (response.success) { if (response.success) {
// 토큰을 쿠키에 저장 // 토큰을 쿠키에 저장
const token = useCookie('auth-token', { const token = useCookie("auth-token", {
maxAge: 60 * 60 * 24, // 24시간 maxAge: 60 * 60 * 24, // 24시간
secure: true, secure: true,
sameSite: 'strict' sameSite: "strict",
}) });
token.value = response.token token.value = response.token;
// 사용자 정보를 쿠키에 저장 // 사용자 정보를 쿠키에 저장
const user = useCookie('user-info', { const user = useCookie("user-info", {
maxAge: 60 * 60 * 24, maxAge: 60 * 60 * 24,
secure: true, secure: true,
sameSite: 'strict' sameSite: "strict",
}) });
user.value = JSON.stringify(response.user) user.value = JSON.stringify(response.user);
// 대시보드로 리다이렉션 // 대시보드로 리다이렉션
await router.push('/dashboard') await router.push("/dashboard");
} else { } else {
errorMessage.value = response.message || '로그인에 실패했습니다.' errorMessage.value = response.message || "로그인에 실패했습니다.";
} }
} catch (error: any) { } catch (error: any) {
console.error('Login error:', error) console.error("Login error:", error);
console.error('Error details:', { console.error("Error details:", {
statusCode: error.statusCode, statusCode: error.statusCode,
statusMessage: error.statusMessage, statusMessage: error.statusMessage,
data: error.data data: error.data,
}) });
errorMessage.value = error.data?.statusMessage || error.statusMessage || '로그인 중 오류가 발생했습니다.' errorMessage.value =
error.data?.statusMessage ||
error.statusMessage ||
"로그인 중 오류가 발생했습니다.";
} finally { } finally {
isLoading.value = false isLoading.value = false;
}
} }
};
definePageMeta({ definePageMeta({
layout: 'blank', layout: "blank",
middleware: 'guest' middleware: "guest",
}) });
</script> </script>
<template> <template>
@@ -106,38 +103,28 @@ definePageMeta({
:class="$vuetify.display.smAndUp ? 'pa-6' : 'pa-0'" :class="$vuetify.display.smAndUp ? 'pa-6' : 'pa-0'"
> >
<VCardItem class="justify-center"> <VCardItem class="justify-center">
<NuxtLink <NuxtLink to="/" class="app-logo">
to="/"
class="app-logo"
>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div <!-- <div
class="d-flex" class="d-flex"
v-html="logo" v-html="logo"
/> />
<h1 class="app-logo-title"> <h1 class="app-logo-title">
sneat sneat
</h1> </h1> -->
</NuxtLink> </NuxtLink>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<h4 class="text-h4 mb-1"> <h4 class="text-h4 mb-1">음악 관리 시스템👋🏻</h4>
음악 관리 시스템👋🏻 <p class="mb-0">계정에 로그인하여 관리 시스템을 시작하세요</p>
</h4>
<p class="mb-0">
계정에 로그인하여 관리 시스템을 시작하세요
</p>
</VCardText> </VCardText>
<VCardText> <VCardText>
<VForm @submit.prevent="handleLogin"> <VForm @submit.prevent="handleLogin">
<VRow> <VRow>
<!-- 오류 메시지 --> <!-- 오류 메시지 -->
<VCol <VCol v-if="errorMessage" cols="12">
v-if="errorMessage"
cols="12"
>
<VAlert <VAlert
type="error" type="error"
variant="tonal" variant="tonal"

View File

@@ -0,0 +1,20 @@
module.exports = {
apps: [
{
name: "music-admin-webhook",
script: "webhook-server.cjs",
cwd: "/var/www/music/music-admin",
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: "100M",
env: {
NODE_ENV: "production",
},
error_file: "/var/log/music-admin-webhook-error.log",
out_file: "/var/log/music-admin-webhook-out.log",
log_file: "/var/log/music-admin-webhook.log",
time: true,
},
],
};

View File

@@ -1,10 +1,10 @@
const express = require("express"); import express from "express";
const { exec } = require("child_process"); import { exec } from "child_process";
const crypto = require("crypto"); import crypto from "crypto";
const app = express(); const app = express();
const PORT = 9000; const PORT = 9000;
const SECRET = "your-webhook-secret-key"; // 보안을 위해 변경하세요 const SECRET = "vTEq9OTvIFs3ZbDaszLRL/ZiAEXziemX1Wh1GIeb+DI="; // 보안을 위해 변경하세요
const PROJECT_DIR = "/var/www/music/music-admin"; const PROJECT_DIR = "/var/www/music/music-admin";
// JSON 파싱 미들웨어 // JSON 파싱 미들웨어