diff --git a/backend/src/main/resources/static/assets/AdminDashboard-B5ryl_KI.js b/backend/src/main/resources/static/assets/AdminDashboard-B5ryl_KI.js new file mode 100644 index 0000000..ee3f847 --- /dev/null +++ b/backend/src/main/resources/static/assets/AdminDashboard-B5ryl_KI.js @@ -0,0 +1 @@ +import{r as a,j as e,L as n}from"./index-ChpGil2q.js";const h=s=>fetch(s,{headers:{Authorization:`Bearer ${localStorage.getItem("admin_token")}`}}).then(l=>l.json());function x(){var t,r;const[s,l]=a.useState(null),[c,d]=a.useState(!0);if(a.useEffect(()=>{h("/api/admin/dashboard").then(l).finally(()=>d(!1))},[]),c)return e.jsx("p",{style:{color:"#64748b",fontSize:14},children:"๋กœ๋”ฉ ์ค‘..."});if(!s)return null;const o=[{icon:"๐Ÿ“ฐ",label:"์ „์ฒด ๋‰ด์Šค",value:s.totalNews,sub:`๊ณต๊ฐœ ${s.visibleNews}๊ฑด`,color:"blue"},{icon:"๐Ÿ“ฉ",label:"์ „์ฒด ๋ฌธ์˜",value:s.totalInquiries,sub:`๋ฏธ๋‹ต๋ณ€ ${s.pendingInquiries}๊ฑด`,color:s.pendingInquiries>0?"red":"green"},{icon:"๐Ÿ‘ฅ",label:"์ฑ„์šฉ๊ณต๊ณ ",value:s.totalRecruits,sub:`์ง„ํ–‰์ค‘ ${s.activeRecruits}๊ฑด`,color:"green"}];return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"admin-stats",children:[o.map(i=>e.jsxs("div",{className:"stat-card",children:[e.jsx("div",{className:`stat-icon ${i.color}`,children:i.icon}),e.jsxs("div",{className:"stat-info",children:[e.jsx("h4",{children:i.value}),e.jsxs("p",{children:[i.label,e.jsx("br",{}),e.jsx("span",{style:{fontSize:11},children:i.sub})]})]})]},i.label)),s.pendingInquiries>0&&e.jsxs("div",{className:"stat-card",style:{borderLeft:"3px solid #ef4444"},children:[e.jsx("div",{className:"stat-icon red",children:"๐Ÿ””"}),e.jsxs("div",{className:"stat-info",children:[e.jsx("h4",{style:{color:"#ef4444"},children:s.pendingInquiries}),e.jsxs("p",{children:["๋ฏธ๋‹ต๋ณ€ ๋ฌธ์˜",e.jsx("br",{}),e.jsx(n,{to:"/admin/inquiries",style:{fontSize:11,color:"#ef4444"},children:"๋ฐ”๋กœ๊ฐ€๊ธฐ โ†’"})]})]})]})]}),e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:16},children:[e.jsxs("div",{className:"admin-card",children:[e.jsxs("div",{className:"admin-card-header",children:[e.jsx("h3",{children:"๐Ÿ“ฐ ์ตœ๊ทผ ๋‰ด์Šค"}),e.jsx(n,{to:"/admin/news",className:"btn btn-outline btn-sm",children:"์ „์ฒด๋ณด๊ธฐ"})]}),e.jsxs("ul",{className:"recent-list",children:[(s.recentNews||[]).map(i=>e.jsxs("li",{children:[e.jsx("span",{className:"rl-dot"}),e.jsx("span",{className:"rl-title",children:i.title}),e.jsx("span",{className:"rl-meta",children:i.category})]},i.id)),!((t=s.recentNews)!=null&&t.length)&&e.jsx("li",{style:{color:"#94a3b8",fontSize:13},children:"๋“ฑ๋ก๋œ ๋‰ด์Šค๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."})]})]}),e.jsxs("div",{className:"admin-card",children:[e.jsxs("div",{className:"admin-card-header",children:[e.jsx("h3",{children:"๐Ÿ“ฉ ์ตœ๊ทผ ๋ฌธ์˜"}),e.jsx(n,{to:"/admin/inquiries",className:"btn btn-outline btn-sm",children:"์ „์ฒด๋ณด๊ธฐ"})]}),e.jsxs("ul",{className:"recent-list",children:[(s.recentInquiries||[]).map(i=>e.jsxs("li",{children:[e.jsx("span",{className:"rl-dot",style:{background:i.status==="PENDING"?"#ef4444":"#22c55e"}}),e.jsx("span",{className:"rl-title",children:i.subject}),e.jsx("span",{className:"rl-meta",children:i.name})]},i.id)),!((r=s.recentInquiries)!=null&&r.length)&&e.jsx("li",{style:{color:"#94a3b8",fontSize:13},children:"์ ‘์ˆ˜๋œ ๋ฌธ์˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."})]})]})]})]})}export{x as default}; diff --git a/backend/src/main/resources/static/assets/AdminInquiry-BBFBdE8S.js b/backend/src/main/resources/static/assets/AdminInquiry-BBFBdE8S.js new file mode 100644 index 0000000..448067b --- /dev/null +++ b/backend/src/main/resources/static/assets/AdminInquiry-BBFBdE8S.js @@ -0,0 +1 @@ +import{r as c,j as e}from"./index-ChpGil2q.js";const E=()=>localStorage.getItem("admin_token"),j=(a,l={})=>fetch(a,{...l,headers:{"Content-Type":"application/json",Authorization:`Bearer ${E()}`,...l.headers}}),b={PENDING:"๋ฏธ๋‹ต๋ณ€",ANSWERED:"๋‹ต๋ณ€์™„๋ฃŒ",CLOSED:"์ข…๊ฒฐ"},g={PENDING:"badge-red",ANSWERED:"badge-green",CLOSED:"badge-gray"};function D(){var p;const[a,l]=c.useState(0),[o,N]=c.useState(""),[i,f]=c.useState({content:[],totalPages:0,totalElements:0}),[n,d]=c.useState(null),[h,u]=c.useState(null),S=(s,t="success")=>{u({msg:s,type:t}),setTimeout(()=>u(null),2500)},m=c.useCallback(()=>{const s=o?`&status=${o}`:"";j(`/api/admin/inquiries?page=${a}&size=10${s}`).then(t=>t.json()).then(f)},[a,o]);c.useEffect(()=>{m()},[m]);const r=async(s,t)=>{(await j(`/api/admin/inquiries/${s}/status`,{method:"PATCH",body:JSON.stringify({status:t})})).ok&&(m(),(n==null?void 0:n.id)===s&&d(v=>({...v,status:t})),S("์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."))},y=async s=>{const t=await j(`/api/admin/inquiries/${s}`);t.ok&&d(await t.json())};return e.jsxs(e.Fragment,{children:[h&&e.jsx("div",{className:"admin-toast",children:e.jsx("div",{className:`toast-item ${h.type}`,children:h.msg})}),e.jsxs("div",{className:"admin-card",children:[e.jsxs("div",{className:"admin-toolbar",children:[e.jsxs("span",{style:{fontSize:13,color:"#64748b"},children:["์ „์ฒด ",i.totalElements,"๊ฑด"]}),e.jsxs("select",{className:"admin-select",value:o,onChange:s=>{N(s.target.value),l(0)},children:[e.jsx("option",{value:"",children:"์ „์ฒด ์ƒํƒœ"}),e.jsx("option",{value:"PENDING",children:"๋ฏธ๋‹ต๋ณ€"}),e.jsx("option",{value:"ANSWERED",children:"๋‹ต๋ณ€์™„๋ฃŒ"}),e.jsx("option",{value:"CLOSED",children:"์ข…๊ฒฐ"})]})]}),e.jsx("div",{className:"admin-table-wrap",children:e.jsxs("table",{className:"admin-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"No"}),e.jsx("th",{children:"์ด๋ฆ„"}),e.jsx("th",{children:"์ œ๋ชฉ"}),e.jsx("th",{children:"์นดํ…Œ๊ณ ๋ฆฌ"}),e.jsx("th",{children:"์ƒํƒœ"}),e.jsx("th",{children:"์ ‘์ˆ˜์ผ"}),e.jsx("th",{children:"๊ด€๋ฆฌ"})]})}),e.jsxs("tbody",{children:[i.content.map((s,t)=>{var x;return e.jsxs("tr",{style:{cursor:"pointer"},children:[e.jsx("td",{style:{color:"#94a3b8",fontSize:12},children:i.totalElements-a*10-t}),e.jsx("td",{children:s.name}),e.jsx("td",{onClick:()=>y(s.id),children:e.jsx("span",{className:"truncate",style:{display:"block",color:"#4f6ef7",cursor:"pointer"},children:s.subject})}),e.jsx("td",{children:e.jsx("span",{className:"badge badge-blue",children:s.category||"๊ธฐํƒ€"})}),e.jsx("td",{children:e.jsx("span",{className:`badge ${g[s.status]||"badge-gray"}`,children:b[s.status]||s.status})}),e.jsx("td",{style:{fontSize:12,color:"#94a3b8"},children:(x=s.createdAt)==null?void 0:x.slice(0,10)}),e.jsx("td",{children:e.jsxs("div",{className:"action-btns",children:[s.status==="PENDING"&&e.jsx("button",{className:"btn btn-outline btn-sm",onClick:()=>r(s.id,"ANSWERED"),children:"๋‹ต๋ณ€์™„๋ฃŒ"}),s.status!=="CLOSED"&&e.jsx("button",{className:"btn btn-outline btn-sm",onClick:()=>r(s.id,"CLOSED"),children:"์ข…๊ฒฐ"})]})})]},s.id)}),!i.content.length&&e.jsx("tr",{children:e.jsx("td",{colSpan:7,children:e.jsxs("div",{className:"empty-state",children:[e.jsx("div",{className:"empty-icon",children:"๐Ÿ“ฉ"}),e.jsx("p",{children:"์ ‘์ˆ˜๋œ ๋ฌธ์˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."})]})})})]})]})}),i.totalPages>1&&e.jsxs("div",{className:"admin-pagination",children:[e.jsxs("span",{className:"admin-pagination-info",children:["ํŽ˜์ด์ง€ ",a+1," / ",i.totalPages]}),e.jsxs("div",{className:"pagination-btns",children:[e.jsx("button",{disabled:a===0,onClick:()=>l(s=>s-1),children:"โ€น"}),Array.from({length:Math.min(i.totalPages,7)},(s,t)=>e.jsx("button",{className:a===t?"active":"",onClick:()=>l(t),children:t+1},t)),e.jsx("button",{disabled:a>=i.totalPages-1,onClick:()=>l(s=>s+1),children:"โ€บ"})]})]})]}),n&&e.jsx("div",{className:"modal-backdrop",onClick:s=>s.target===s.currentTarget&&d(null),children:e.jsxs("div",{className:"modal",children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"๋ฌธ์˜ ์ƒ์„ธ"}),e.jsx("button",{onClick:()=>d(null),children:"โœ•"})]}),e.jsxs("div",{className:"modal-body",children:[e.jsx("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:12,marginBottom:16},children:[["์ด๋ฆ„",n.name],["์ด๋ฉ”์ผ",n.email],["์—ฐ๋ฝ์ฒ˜",n.phone||"-"],["์œ ํ˜•",n.category||"๊ธฐํƒ€"]].map(([s,t])=>e.jsxs("div",{children:[e.jsx("div",{style:{fontSize:11,fontWeight:600,color:"#64748b",marginBottom:3,textTransform:"uppercase"},children:s}),e.jsx("div",{style:{fontSize:13.5},children:t})]},s))}),e.jsxs("div",{style:{marginBottom:12},children:[e.jsx("div",{style:{fontSize:11,fontWeight:600,color:"#64748b",marginBottom:4,textTransform:"uppercase"},children:"์ œ๋ชฉ"}),e.jsx("div",{style:{fontWeight:600,fontSize:15},children:n.subject})]}),e.jsxs("div",{children:[e.jsx("div",{style:{fontSize:11,fontWeight:600,color:"#64748b",marginBottom:6,textTransform:"uppercase"},children:"๋‚ด์šฉ"}),e.jsx("div",{style:{background:"#f8fafc",borderRadius:8,padding:"14px 16px",fontSize:13.5,lineHeight:1.7,whiteSpace:"pre-wrap",border:"1px solid #e2e8f0"},children:n.content})]}),e.jsxs("div",{style:{marginTop:16,display:"flex",alignItems:"center",gap:8},children:[e.jsxs("span",{style:{fontSize:12,color:"#64748b"},children:["์ ‘์ˆ˜์ผ: ",(p=n.createdAt)==null?void 0:p.slice(0,16)]}),e.jsx("span",{className:`badge ${g[n.status]||"badge-gray"}`,children:b[n.status]})]})]}),e.jsxs("div",{className:"modal-footer",children:[n.status==="PENDING"&&e.jsx("button",{className:"btn btn-primary",onClick:()=>r(n.id,"ANSWERED"),children:"๋‹ต๋ณ€์™„๋ฃŒ ์ฒ˜๋ฆฌ"}),n.status!=="CLOSED"&&e.jsx("button",{className:"btn btn-outline",onClick:()=>r(n.id,"CLOSED"),children:"์ข…๊ฒฐ"}),e.jsx("button",{className:"btn btn-outline",onClick:()=>d(null),children:"๋‹ซ๊ธฐ"})]})]})})]})}export{D as default}; diff --git a/backend/src/main/resources/static/assets/AdminLayout-uWX9KsdF.js b/backend/src/main/resources/static/assets/AdminLayout-uWX9KsdF.js new file mode 100644 index 0000000..2419936 --- /dev/null +++ b/backend/src/main/resources/static/assets/AdminLayout-uWX9KsdF.js @@ -0,0 +1 @@ +import{c as g,u as x,r as s,j as a,N as b,O as j}from"./index-ChpGil2q.js";/* empty css */const N=[{section:"๋ฉ”์ธ"},{path:"/admin/dashboard",icon:"๐Ÿ“Š",label:"๋Œ€์‹œ๋ณด๋“œ"},{section:"์ฝ˜ํ…์ธ  ๊ด€๋ฆฌ"},{path:"/admin/news",icon:"๐Ÿ“ฐ",label:"๋‰ด์Šค/๊ณต์ง€์‚ฌํ•ญ"},{path:"/admin/recruit",icon:"๐Ÿ‘ฅ",label:"์ฑ„์šฉ๊ณต๊ณ "},{section:"๊ณ ๊ฐ ๊ด€๋ฆฌ"},{path:"/admin/inquiries",icon:"๐Ÿ“ฉ",label:"๋ฌธ์˜ ๊ด€๋ฆฌ",badgeKey:"pendingInquiries"},{section:"์‹œ์Šคํ…œ"},{path:"/admin/settings",icon:"โš™๏ธ",label:"์„ค์ •"}];function y(){const i=g(),d=x(),[t,r]=s.useState(null),[l,m]=s.useState("๋Œ€์‹œ๋ณด๋“œ"),[o,h]=s.useState({});s.useEffect(()=>{const e=localStorage.getItem("admin_token");if(!e){i("/admin/login");return}const n=JSON.parse(localStorage.getItem("admin_user")||"{}");r(n),u(e)},[i]),s.useEffect(()=>{m({"/admin/dashboard":"๋Œ€์‹œ๋ณด๋“œ","/admin/news":"๋‰ด์Šค/๊ณต์ง€์‚ฌํ•ญ ๊ด€๋ฆฌ","/admin/inquiries":"๋ฌธ์˜ ๊ด€๋ฆฌ","/admin/recruit":"์ฑ„์šฉ๊ณต๊ณ  ๊ด€๋ฆฌ","/admin/settings":"์„ค์ •"}[d.pathname]||"๊ด€๋ฆฌ์ž")},[d.pathname]);const u=async e=>{try{const n=await fetch("/api/admin/dashboard",{headers:{Authorization:`Bearer ${e}`}});if(n.ok){const c=await n.json();h({pendingInquiries:c.pendingInquiries||0})}}catch{}},p=()=>{localStorage.removeItem("admin_token"),localStorage.removeItem("admin_user"),i("/admin/login")};return t?a.jsxs("div",{className:"admin-wrap",children:[a.jsxs("aside",{className:"admin-sidebar",children:[a.jsxs("div",{className:"admin-sidebar-logo",children:[a.jsx("h2",{children:"ZioInfo Admin"}),a.jsx("span",{children:"(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ๊ด€๋ฆฌ์ž"})]}),a.jsx("nav",{className:"admin-nav",children:N.map((e,n)=>e.section?a.jsx("div",{className:"admin-nav-section",children:e.section},n):a.jsxs(b,{to:e.path,className:({isActive:c})=>c?"active":"",children:[a.jsx("span",{className:"nav-icon",children:e.icon}),e.label,e.badgeKey&&o[e.badgeKey]>0&&a.jsx("span",{className:"admin-nav-badge",children:o[e.badgeKey]})]},e.path))}),a.jsx("div",{className:"admin-sidebar-footer",children:a.jsx("button",{onClick:p,children:"๐Ÿšช ๋กœ๊ทธ์•„์›ƒ"})})]}),a.jsxs("main",{className:"admin-main",children:[a.jsxs("div",{className:"admin-topbar",children:[a.jsx("h1",{children:l}),a.jsxs("div",{className:"admin-topbar-right",children:[a.jsxs("span",{className:"admin-user-badge",children:["๐Ÿ‘ค ",t.displayName||t.username]}),a.jsx("a",{href:"/",target:"_blank",rel:"noreferrer",style:{fontSize:12,color:"#64748b",textDecoration:"none"},children:"๐ŸŒ ํ™ˆํŽ˜์ด์ง€ ๋ณด๊ธฐ"})]})]}),a.jsx("div",{className:"admin-content",children:a.jsx(j,{})})]})]}):null}export{y as default}; diff --git a/backend/src/main/resources/static/assets/AdminLogin-DcRT5LbX.js b/backend/src/main/resources/static/assets/AdminLogin-DcRT5LbX.js new file mode 100644 index 0000000..d426fba --- /dev/null +++ b/backend/src/main/resources/static/assets/AdminLogin-DcRT5LbX.js @@ -0,0 +1 @@ +import{r as i,c as p,j as e}from"./index-ChpGil2q.js";/* empty css */function x(){const[t,o]=i.useState({username:"",password:""}),[l,r]=i.useState(""),[d,c]=i.useState(!1),m=p(),u=async a=>{a.preventDefault(),r(""),c(!0);try{const s=await fetch("/api/admin/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),n=await s.json();if(!s.ok){r(n.message||"๋กœ๊ทธ์ธ ์‹คํŒจ");return}localStorage.setItem("admin_token",n.token),localStorage.setItem("admin_user",JSON.stringify({username:n.username,displayName:n.displayName})),m("/admin/dashboard")}catch{r("์„œ๋ฒ„ ์—ฐ๊ฒฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.")}finally{c(!1)}};return e.jsx("div",{className:"admin-login-page",children:e.jsxs("div",{className:"admin-login-box",children:[e.jsxs("div",{className:"login-logo",children:[e.jsx("span",{className:"login-badge",children:"ADMIN"}),e.jsx("h1",{children:"(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ "}),e.jsx("p",{children:"ํ™ˆํŽ˜์ด์ง€ ๊ด€๋ฆฌ์ž ์‹œ์Šคํ…œ"})]}),l&&e.jsxs("div",{className:"login-error",children:["โš  ",l]}),e.jsxs("form",{onSubmit:u,children:[e.jsxs("div",{className:"login-input-group",children:[e.jsx("label",{children:"์•„์ด๋””"}),e.jsx("input",{type:"text",placeholder:"๊ด€๋ฆฌ์ž ์•„์ด๋””",value:t.username,required:!0,onChange:a=>o(s=>({...s,username:a.target.value}))})]}),e.jsxs("div",{className:"login-input-group",children:[e.jsx("label",{children:"๋น„๋ฐ€๋ฒˆํ˜ธ"}),e.jsx("input",{type:"password",placeholder:"๋น„๋ฐ€๋ฒˆํ˜ธ",value:t.password,required:!0,onChange:a=>o(s=>({...s,password:a.target.value}))})]}),e.jsx("button",{type:"submit",className:"login-btn",disabled:d,children:d?"๋กœ๊ทธ์ธ ์ค‘...":"๋กœ๊ทธ์ธ"})]}),e.jsxs("p",{style:{textAlign:"center",marginTop:20,fontSize:12,color:"#94a3b8"},children:["ํ™ˆํŽ˜์ด์ง€๋กœ ๋Œ์•„๊ฐ€๊ธฐ: ",e.jsx("a",{href:"/",style:{color:"#4f6ef7"},children:"๋ฉ”์ธ ํŽ˜์ด์ง€"})]})]})})}export{x as default}; diff --git a/backend/src/main/resources/static/assets/AdminNews-CDSgPR9E.js b/backend/src/main/resources/static/assets/AdminNews-CDSgPR9E.js new file mode 100644 index 0000000..fb4a04d --- /dev/null +++ b/backend/src/main/resources/static/assets/AdminNews-CDSgPR9E.js @@ -0,0 +1 @@ +import{r as l,j as e}from"./index-ChpGil2q.js";const P=()=>localStorage.getItem("admin_token"),x=(a,c={})=>fetch(a,{...c,headers:{"Content-Type":"application/json",Authorization:`Bearer ${P()}`,...c.headers}}),f={title:"",category:"๊ณต์ง€์‚ฌํ•ญ",summary:"",content:"",thumbnailUrl:"",visible:!0},$=["๊ณต์ง€์‚ฌํ•ญ","๋ณด๋„์ž๋ฃŒ","์ด๋ฒคํŠธ"];function z(){const[a,c]=l.useState(0),[n,y]=l.useState({content:[],totalPages:0,totalElements:0}),[C,o]=l.useState(null),[i,j]=l.useState(f),[r,g]=l.useState(null),[p,v]=l.useState(!1),[u,N]=l.useState(null),b=(s,t="success")=>{N({msg:s,type:t}),setTimeout(()=>N(null),2500)},m=l.useCallback(()=>{x(`/api/admin/news?page=${a}&size=10`).then(s=>s.json()).then(y)},[a]);l.useEffect(()=>{m()},[m]);const k=()=>{j(f),g(null),o("form")},S=s=>{j({...s}),g(s.id),o("form")},w=async()=>{v(!0);const s=r?`/api/admin/news/${r}`:"/api/admin/news",h=await x(s,{method:r?"PUT":"POST",body:JSON.stringify(i)});v(!1),h.ok?(o(null),m(),b(r?"์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.":"๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")):b("์ €์žฅ ์‹คํŒจ","error")},E=async s=>{if(!confirm("์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?"))return;(await x(`/api/admin/news/${s}`,{method:"DELETE"})).ok&&(m(),b("์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."))},T=async s=>{await x(`/api/admin/news/${s}/visibility`,{method:"PATCH"}),m()},d=(s,t)=>j(h=>({...h,[s]:t}));return e.jsxs(e.Fragment,{children:[u&&e.jsx("div",{className:"admin-toast",children:e.jsx("div",{className:`toast-item ${u.type}`,children:u.msg})}),e.jsxs("div",{className:"admin-card",children:[e.jsxs("div",{className:"admin-toolbar",children:[e.jsxs("span",{style:{fontSize:13,color:"#64748b"},children:["์ „์ฒด ",n.totalElements,"๊ฑด"]}),e.jsx("div",{className:"admin-toolbar-right",children:e.jsx("button",{className:"btn btn-primary",onClick:k,children:"๏ผ‹ ๋‰ด์Šค ๋“ฑ๋ก"})})]}),e.jsx("div",{className:"admin-table-wrap",children:e.jsxs("table",{className:"admin-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"No"}),e.jsx("th",{children:"์ œ๋ชฉ"}),e.jsx("th",{children:"์นดํ…Œ๊ณ ๋ฆฌ"}),e.jsx("th",{children:"๊ณต๊ฐœ"}),e.jsx("th",{children:"์กฐํšŒ์ˆ˜"}),e.jsx("th",{children:"๋“ฑ๋ก์ผ"}),e.jsx("th",{children:"๊ด€๋ฆฌ"})]})}),e.jsxs("tbody",{children:[n.content.map((s,t)=>{var h;return e.jsxs("tr",{children:[e.jsx("td",{style:{color:"#94a3b8",fontSize:12},children:n.totalElements-a*10-t}),e.jsx("td",{children:e.jsx("span",{className:"truncate",style:{display:"block"},children:s.title})}),e.jsx("td",{children:e.jsx("span",{className:`badge ${s.category==="๋ณด๋„์ž๋ฃŒ"?"badge-blue":s.category==="์ด๋ฒคํŠธ"?"badge-orange":"badge-gray"}`,children:s.category})}),e.jsx("td",{children:e.jsx("button",{onClick:()=>T(s.id),className:`badge ${s.visible?"badge-green":"badge-red"}`,style:{cursor:"pointer",border:"none"},children:s.visible?"๊ณต๊ฐœ":"๋น„๊ณต๊ฐœ"})}),e.jsx("td",{children:s.viewCount}),e.jsx("td",{style:{fontSize:12,color:"#94a3b8"},children:(h=s.createdAt)==null?void 0:h.slice(0,10)}),e.jsx("td",{children:e.jsxs("div",{className:"action-btns",children:[e.jsx("button",{className:"btn btn-outline btn-sm",onClick:()=>S(s),children:"์ˆ˜์ •"}),e.jsx("button",{className:"btn btn-danger btn-sm",onClick:()=>E(s.id),children:"์‚ญ์ œ"})]})})]},s.id)}),!n.content.length&&e.jsx("tr",{children:e.jsx("td",{colSpan:7,children:e.jsxs("div",{className:"empty-state",children:[e.jsx("div",{className:"empty-icon",children:"๐Ÿ“ฐ"}),e.jsx("p",{children:"๋“ฑ๋ก๋œ ๋‰ด์Šค๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."})]})})})]})]})}),n.totalPages>1&&e.jsxs("div",{className:"admin-pagination",children:[e.jsxs("span",{className:"admin-pagination-info",children:["ํŽ˜์ด์ง€ ",a+1," / ",n.totalPages]}),e.jsxs("div",{className:"pagination-btns",children:[e.jsx("button",{disabled:a===0,onClick:()=>c(s=>s-1),children:"โ€น"}),Array.from({length:n.totalPages},(s,t)=>e.jsx("button",{className:a===t?"active":"",onClick:()=>c(t),children:t+1},t)),e.jsx("button",{disabled:a>=n.totalPages-1,onClick:()=>c(s=>s+1),children:"โ€บ"})]})]})]}),C==="form"&&e.jsx("div",{className:"modal-backdrop",onClick:s=>s.target===s.currentTarget&&o(null),children:e.jsxs("div",{className:"modal",children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:r?"๋‰ด์Šค ์ˆ˜์ •":"๋‰ด์Šค ๋“ฑ๋ก"}),e.jsx("button",{onClick:()=>o(null),children:"โœ•"})]}),e.jsxs("div",{className:"modal-body",children:[e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"์ œ๋ชฉ *"}),e.jsx("input",{className:"form-control",value:i.title,onChange:s=>d("title",s.target.value),placeholder:"๋‰ด์Šค ์ œ๋ชฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”"})]}),e.jsxs("div",{className:"form-row",children:[e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"์นดํ…Œ๊ณ ๋ฆฌ"}),e.jsx("select",{className:"form-control",value:i.category,onChange:s=>d("category",s.target.value),children:$.map(s=>e.jsx("option",{children:s},s))})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"๊ณต๊ฐœ ์—ฌ๋ถ€"}),e.jsxs("select",{className:"form-control",value:i.visible,onChange:s=>d("visible",s.target.value==="true"),children:[e.jsx("option",{value:"true",children:"๊ณต๊ฐœ"}),e.jsx("option",{value:"false",children:"๋น„๊ณต๊ฐœ"})]})]})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"์š”์•ฝ"}),e.jsx("input",{className:"form-control",value:i.summary||"",onChange:s=>d("summary",s.target.value),placeholder:"๋ชฉ๋ก์— ํ‘œ์‹œ๋  ์š”์•ฝ ๋ฌธ๊ตฌ"})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"์ธ๋„ค์ผ URL"}),e.jsx("input",{className:"form-control",value:i.thumbnailUrl||"",onChange:s=>d("thumbnailUrl",s.target.value),placeholder:"https://..."})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"๋ณธ๋ฌธ ๋‚ด์šฉ *"}),e.jsx("textarea",{className:"form-control",rows:8,value:i.content||"",onChange:s=>d("content",s.target.value),placeholder:"๋‰ด์Šค ๋ณธ๋ฌธ ๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”"})]})]}),e.jsxs("div",{className:"modal-footer",children:[e.jsx("button",{className:"btn btn-outline",onClick:()=>o(null),children:"์ทจ์†Œ"}),e.jsx("button",{className:"btn btn-primary",onClick:w,disabled:p||!i.title,children:p?"์ €์žฅ ์ค‘...":r?"์ˆ˜์ • ์™„๋ฃŒ":"๋“ฑ๋ก"})]})]})})]})}export{z as default}; diff --git a/backend/src/main/resources/static/assets/AdminRecruit-CfX4mhQb.js b/backend/src/main/resources/static/assets/AdminRecruit-CfX4mhQb.js new file mode 100644 index 0000000..8159333 --- /dev/null +++ b/backend/src/main/resources/static/assets/AdminRecruit-CfX4mhQb.js @@ -0,0 +1 @@ +import{r,j as e}from"./index-ChpGil2q.js";const P=()=>localStorage.getItem("admin_token"),u=(l,c={})=>fetch(l,{...c,headers:{"Content-Type":"application/json",Authorization:`Bearer ${P()}`,...c.headers}}),f={title:"",department:"",jobType:"์ •๊ทœ์ง",description:"",requirements:"",preferred:"",deadline:"",headcount:1,active:!0},w=["์ •๊ทœ์ง","๊ณ„์•ฝ์ง","์ธํ„ด","ํ”„๋ฆฌ๋žœ์„œ"];function z(){const[l,c]=r.useState(0),[i,N]=r.useState({content:[],totalPages:0,totalElements:0}),[y,d]=r.useState(!1),[t,m]=r.useState(f),[o,p]=r.useState(null),[b,g]=r.useState(!1),[j,v]=r.useState(null),x=(s,a="success")=>{v({msg:s,type:a}),setTimeout(()=>v(null),2500)},h=r.useCallback(()=>{u(`/api/admin/recruits?page=${l}&size=10`).then(s=>s.json()).then(N)},[l]);r.useEffect(()=>{h()},[h]);const C=()=>{m(f),p(null),d(!0)},S=s=>{m({...s,deadline:s.deadline||""}),p(s.id),d(!0)},k=async()=>{g(!0);const s=o?`/api/admin/recruits/${o}`:"/api/admin/recruits",a=await u(s,{method:o?"PUT":"POST",body:JSON.stringify(t)});g(!1),a.ok?(d(!1),h(),x(o?"์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.":"๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")):x("์ €์žฅ ์‹คํŒจ","error")},T=async s=>{if(!confirm("์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?"))return;(await u(`/api/admin/recruits/${s}`,{method:"DELETE"})).ok&&(h(),x("์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."))},n=(s,a)=>m(E=>({...E,[s]:a}));return e.jsxs(e.Fragment,{children:[j&&e.jsx("div",{className:"admin-toast",children:e.jsx("div",{className:`toast-item ${j.type}`,children:j.msg})}),e.jsxs("div",{className:"admin-card",children:[e.jsxs("div",{className:"admin-toolbar",children:[e.jsxs("span",{style:{fontSize:13,color:"#64748b"},children:["์ „์ฒด ",i.totalElements,"๊ฑด"]}),e.jsx("div",{className:"admin-toolbar-right",children:e.jsx("button",{className:"btn btn-primary",onClick:C,children:"๏ผ‹ ์ฑ„์šฉ๊ณต๊ณ  ๋“ฑ๋ก"})})]}),e.jsx("div",{className:"admin-table-wrap",children:e.jsxs("table",{className:"admin-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"No"}),e.jsx("th",{children:"๊ณต๊ณ ๋ช…"}),e.jsx("th",{children:"๋ถ€์„œ"}),e.jsx("th",{children:"์œ ํ˜•"}),e.jsx("th",{children:"๋ชจ์ง‘์ธ์›"}),e.jsx("th",{children:"๋งˆ๊ฐ์ผ"}),e.jsx("th",{children:"์ƒํƒœ"}),e.jsx("th",{children:"๊ด€๋ฆฌ"})]})}),e.jsxs("tbody",{children:[i.content.map((s,a)=>e.jsxs("tr",{children:[e.jsx("td",{style:{color:"#94a3b8",fontSize:12},children:i.totalElements-l*10-a}),e.jsx("td",{children:e.jsx("span",{className:"truncate",style:{display:"block"},children:s.title})}),e.jsx("td",{children:s.department||"-"}),e.jsx("td",{children:e.jsx("span",{className:`badge ${s.jobType==="์ •๊ทœ์ง"?"badge-blue":s.jobType==="์ธํ„ด"?"badge-orange":"badge-gray"}`,children:s.jobType})}),e.jsxs("td",{children:[s.headcount,"๋ช…"]}),e.jsx("td",{style:{fontSize:12},children:s.deadline||"์ƒ์‹œ"}),e.jsx("td",{children:e.jsx("span",{className:`badge ${s.active?"badge-green":"badge-red"}`,children:s.active?"์ง„ํ–‰์ค‘":"๋งˆ๊ฐ"})}),e.jsx("td",{children:e.jsxs("div",{className:"action-btns",children:[e.jsx("button",{className:"btn btn-outline btn-sm",onClick:()=>S(s),children:"์ˆ˜์ •"}),e.jsx("button",{className:"btn btn-danger btn-sm",onClick:()=>T(s.id),children:"์‚ญ์ œ"})]})})]},s.id)),!i.content.length&&e.jsx("tr",{children:e.jsx("td",{colSpan:8,children:e.jsxs("div",{className:"empty-state",children:[e.jsx("div",{className:"empty-icon",children:"๐Ÿ‘ฅ"}),e.jsx("p",{children:"๋“ฑ๋ก๋œ ์ฑ„์šฉ๊ณต๊ณ ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."})]})})})]})]})}),i.totalPages>1&&e.jsxs("div",{className:"admin-pagination",children:[e.jsxs("span",{className:"admin-pagination-info",children:["ํŽ˜์ด์ง€ ",l+1," / ",i.totalPages]}),e.jsxs("div",{className:"pagination-btns",children:[e.jsx("button",{disabled:l===0,onClick:()=>c(s=>s-1),children:"โ€น"}),Array.from({length:i.totalPages},(s,a)=>e.jsx("button",{className:l===a?"active":"",onClick:()=>c(a),children:a+1},a)),e.jsx("button",{disabled:l>=i.totalPages-1,onClick:()=>c(s=>s+1),children:"โ€บ"})]})]})]}),y&&e.jsx("div",{className:"modal-backdrop",onClick:s=>s.target===s.currentTarget&&d(!1),children:e.jsxs("div",{className:"modal",children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:o?"์ฑ„์šฉ๊ณต๊ณ  ์ˆ˜์ •":"์ฑ„์šฉ๊ณต๊ณ  ๋“ฑ๋ก"}),e.jsx("button",{onClick:()=>d(!1),children:"โœ•"})]}),e.jsxs("div",{className:"modal-body",children:[e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"๊ณต๊ณ  ์ œ๋ชฉ *"}),e.jsx("input",{className:"form-control",value:t.title,onChange:s=>n("title",s.target.value),placeholder:"์˜ˆ: ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž (Java/Spring)"})]}),e.jsxs("div",{className:"form-row",children:[e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"๋ถ€์„œ"}),e.jsx("input",{className:"form-control",value:t.department,onChange:s=>n("department",s.target.value),placeholder:"๊ฐœ๋ฐœํŒ€, ์˜์—…ํŒ€ ๋“ฑ"})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"๊ณ ์šฉํ˜•ํƒœ"}),e.jsx("select",{className:"form-control",value:t.jobType,onChange:s=>n("jobType",s.target.value),children:w.map(s=>e.jsx("option",{children:s},s))})]})]}),e.jsxs("div",{className:"form-row",children:[e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"๋ชจ์ง‘ ์ธ์›"}),e.jsx("input",{type:"number",min:1,className:"form-control",value:t.headcount,onChange:s=>n("headcount",parseInt(s.target.value))})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"๋งˆ๊ฐ์ผ"}),e.jsx("input",{type:"date",className:"form-control",value:t.deadline,onChange:s=>n("deadline",s.target.value)})]})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"๊ณต๊ณ  ์ƒํƒœ"}),e.jsxs("select",{className:"form-control",value:t.active,onChange:s=>n("active",s.target.value==="true"),children:[e.jsx("option",{value:"true",children:"์ง„ํ–‰์ค‘"}),e.jsx("option",{value:"false",children:"๋งˆ๊ฐ"})]})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"๋‹ด๋‹น ์—…๋ฌด"}),e.jsx("textarea",{className:"form-control",rows:4,value:t.description,onChange:s=>n("description",s.target.value),placeholder:"- ์ฃผ์š” ๋‹ด๋‹น ์—…๋ฌด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”"})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"์ง€์› ์ž๊ฒฉ"}),e.jsx("textarea",{className:"form-control",rows:4,value:t.requirements,onChange:s=>n("requirements",s.target.value),placeholder:"- ํ•„์ˆ˜ ์ž๊ฒฉ์š”๊ฑด์„ ์ž…๋ ฅํ•˜์„ธ์š”"})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"์šฐ๋Œ€ ์‚ฌํ•ญ"}),e.jsx("textarea",{className:"form-control",rows:3,value:t.preferred,onChange:s=>n("preferred",s.target.value),placeholder:"- ์šฐ๋Œ€ ์‚ฌํ•ญ์„ ์ž…๋ ฅํ•˜์„ธ์š”"})]})]}),e.jsxs("div",{className:"modal-footer",children:[e.jsx("button",{className:"btn btn-outline",onClick:()=>d(!1),children:"์ทจ์†Œ"}),e.jsx("button",{className:"btn btn-primary",onClick:k,disabled:b||!t.title,children:b?"์ €์žฅ ์ค‘...":o?"์ˆ˜์ • ์™„๋ฃŒ":"๋“ฑ๋ก"})]})]})})]})}export{z as default}; diff --git a/backend/src/main/resources/static/assets/AdminSettings-DaHEGHsg.js b/backend/src/main/resources/static/assets/AdminSettings-DaHEGHsg.js new file mode 100644 index 0000000..7465e66 --- /dev/null +++ b/backend/src/main/resources/static/assets/AdminSettings-DaHEGHsg.js @@ -0,0 +1 @@ +import{c as u,r as d,j as e}from"./index-ChpGil2q.js";const h=()=>localStorage.getItem("admin_token");function f(){const m=u(),i=JSON.parse(localStorage.getItem("admin_user")||"{}"),[r,n]=d.useState({currentPassword:"",newPassword:"",confirmPassword:""}),[t,o]=d.useState(null),[l,c]=d.useState(!1),p=async()=>{if(r.newPassword!==r.confirmPassword){o({text:"์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.",type:"error"});return}if(r.newPassword.length<8){o({text:"๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” 8์ž ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",type:"error"});return}c(!0);const s=await fetch("/api/admin/password",{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${h()}`},body:JSON.stringify({currentPassword:r.currentPassword,newPassword:r.newPassword})}),a=await s.json();c(!1),s.ok?(o({text:"๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•ด์ฃผ์„ธ์š”.",type:"success"}),n({currentPassword:"",newPassword:"",confirmPassword:""}),setTimeout(()=>{localStorage.removeItem("admin_token"),m("/admin/login")},2e3)):o({text:a.message||"๋ณ€๊ฒฝ ์‹คํŒจ",type:"error"})};return e.jsxs("div",{style:{maxWidth:520},children:[e.jsxs("div",{className:"admin-card",style:{marginBottom:20},children:[e.jsx("div",{className:"admin-card-header",children:e.jsx("h3",{children:"๐Ÿ‘ค ๊ณ„์ • ์ •๋ณด"})}),e.jsx("div",{style:{display:"grid",gap:12},children:[["์•„์ด๋””",i.username],["ํ‘œ์‹œ ์ด๋ฆ„",i.displayName||"-"]].map(([s,a])=>e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12},children:[e.jsx("span",{style:{fontSize:12,fontWeight:600,color:"#64748b",width:80},children:s}),e.jsx("span",{style:{fontSize:14},children:a})]},s))})]}),e.jsxs("div",{className:"admin-card",children:[e.jsx("div",{className:"admin-card-header",children:e.jsx("h3",{children:"๐Ÿ”’ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ"})}),t&&e.jsx("div",{style:{padding:"10px 14px",borderRadius:7,marginBottom:16,fontSize:13,background:t.type==="error"?"#fff1f2":"#f0fdf4",color:t.type==="error"?"#dc2626":"#16a34a",border:`1px solid ${t.type==="error"?"#fecaca":"#bbf7d0"}`},children:t.text}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"ํ˜„์žฌ ๋น„๋ฐ€๋ฒˆํ˜ธ"}),e.jsx("input",{type:"password",className:"form-control",value:r.currentPassword,onChange:s=>n(a=>({...a,currentPassword:s.target.value}))})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ"}),e.jsx("input",{type:"password",className:"form-control",value:r.newPassword,placeholder:"8์ž ์ด์ƒ",onChange:s=>n(a=>({...a,newPassword:s.target.value}))})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ"}),e.jsx("input",{type:"password",className:"form-control",value:r.confirmPassword,onChange:s=>n(a=>({...a,confirmPassword:s.target.value}))})]}),e.jsx("button",{className:"btn btn-primary",onClick:p,disabled:l||!r.currentPassword||!r.newPassword,children:l?"๋ณ€๊ฒฝ ์ค‘...":"๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ"})]})]})}export{f as default}; diff --git a/backend/src/main/resources/static/assets/Business-EGnXphuY.js b/backend/src/main/resources/static/assets/Business-EGnXphuY.js new file mode 100644 index 0000000..4af58da --- /dev/null +++ b/backend/src/main/resources/static/assets/Business-EGnXphuY.js @@ -0,0 +1 @@ +import{j as e,b as h,a as i,r as j,N as g}from"./index-ChpGil2q.js";/* empty css */const m=[{path:"/business/reference",label:"๊ตฌ์ถ• ๋ ˆํผ๋Ÿฐ์Šค"},{path:"/business/partner",label:"ํŒŒํŠธ๋„ˆ"}];function p({title:c}){return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"page-hero",children:e.jsxs("div",{className:"container",children:[e.jsx("span",{className:"section-label",children:"Business"}),e.jsx("h1",{className:"page-hero-title",children:c})]})}),e.jsx("nav",{className:"sub-nav",children:e.jsx("div",{className:"container",children:m.map(a=>e.jsx(g,{to:a.path,className:({isActive:l})=>"sub-nav-item"+(l?" active":""),children:a.label},a.path))})})]})}const t=[{period:"24.12~25.02",client:"์— ๋กœ",project:"DELL ์ฐจ์„ธ๋Œ€ CRM ๊ตฌ์ถ•",role:"DBA",tech:"Oracle 19C, SQL/PLSQL, Java",category:"๊ธˆ์œตยท์ œ์กฐ"},{period:"24.09~25.10",client:"์‚ผ์„ฑ์ „์ž",project:"์‚ผ์„ฑ์ „์ž ์ฐจ์„ธ๋Œ€ CRM ๊ตฌ์ถ•",role:"DBํŠœ๋„ˆ",tech:"JBOSS, EDB, SQL/PLSQL, Java",category:"๋Œ€๊ธฐ์—…"},{period:"24.03~24.06",client:"์„œ์šธ์‹ ์šฉ๋ณด์ฆ์žฌ๋‹จ",project:"์†Œ์ƒ๊ณต์ธ ์ปจ์„คํŒ…์‹œ์Šคํ…œ ๊ตฌ์ถ•",role:"PM",tech:"JSP/Java, Websquare, Spring, Oracle",category:"๊ณต๊ณต๊ธฐ๊ด€"},{period:"23.11~24.02",client:"๊ตญ๋ฏผ์—ฐ๊ธˆ๊ด€๋ฆฌ๊ณต๋‹จ",project:"๊ตญ๋ฏผ์—ฐ๊ธˆ ์ฐจ์„ธ๋Œ€ ์‹œ์Šคํ…œ ๊ตฌ์ถ•",role:"AA",tech:"JSP/Java, Nexacro, Spring, CI/CD",category:"๊ณต๊ณต๊ธฐ๊ด€"},{period:"23.08~23.10",client:"ํ—Œ๋ฒ•์žฌํŒ์†Œ",project:"ํ—Œ๋ฒ•์žฌํŒ์†Œ ํฌํ„ธ์‹œ์Šคํ…œ ๊ตฌ์ถ•",role:"PM",tech:"Java, Egov, Spring, JEUS",category:"๊ณต๊ณต๊ธฐ๊ด€"},{period:"22.08~23.07",client:"์„œ์šธ์‹ ์šฉ๋ณด์ฆ์žฌ๋‹จ",project:"์žฌ๋‹จ ๋ชจ๋ฐ”์ผ์•ฑ ๊ตฌ์ถ•",role:"PM",tech:"Java, Nexacro, Spring, EDB",category:"๊ณต๊ณต๊ธฐ๊ด€"},{period:"22.01~22.07",client:"์—์ดํ…์—์ดํ”ผ",project:"ํ†ตํ•ฉ์œ ์ง€๋ณด์ˆ˜๊ด€๋ฆฌ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ",role:"PM",tech:"Java, Nexacro, Spring, Tomcat",category:"IT์„œ๋น„์Šค"},{period:"21.10~21.12",client:"ํ—Œ๋ฒ•์žฌํŒ์†Œ",project:"ํ†ตํ•ฉ๋ณด์•ˆ๊ด€์ œ์‹œ์Šคํ…œ ๊ตฌ์ถ• / DB์ด๊ด€",role:"PM",tech:"Java, Spring, JEUS, Oracle 12c",category:"๊ณต๊ณต๊ธฐ๊ด€"},{period:"20.05~21.09",client:"ํ˜„๋Œ€๋ฐฑํ™”์ ",project:"ํ˜„๋Œ€๋ฐฑํ™”์  HKOS ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ",role:"PM",tech:"Java, Nexacro, Spring, Pro*C",category:"์œ ํ†ตยท๋ฌผ๋ฅ˜"},{period:"20.12~21.04",client:"์„œ์šธ์‹œ๋ฆฝ๋Œ€",project:"๋Œ€ํ•™ํ–‰์ •์ •๋ณด์‹œ์Šคํ…œ ์„ฑ๋Šฅ๊ฐœ์„ ",role:"PL",tech:"Java, Spring, JMeter, JEUS, OZ",category:"๊ต์œก๊ธฐ๊ด€"},{period:"20.07~20.11",client:"์—์ดํ…์—์ดํ”ผ",project:"WMS ๊ณตํ†ต ํ”„๋ ˆ์ž„์›Œํฌ ๊ตฌ์ถ•",role:"PM",tech:"Java, Spring, Nexacro, Oracle",category:"IT์„œ๋น„์Šค"},{period:"20.01~20.06",client:"๋†ํ˜‘ํ•˜๋‚˜๋กœ๋งˆํŠธ",project:"๋†ํ˜‘ ํ•˜๋‚˜๋กœ๋งˆํŠธ ESL์‹œ์Šคํ…œ",role:"PM",tech:"C#, Java Spring Batch, REST API",category:"์œ ํ†ตยท๋ฌผ๋ฅ˜"},{period:"19.07~19.12",client:"์žฅ๋ณด๊ณ ์‹์ž์žฌ๋งˆํŠธ",project:"์ •์‚ฐ์‹œ์Šคํ…œ ๊ตฌ์ถ•",role:"PM",tech:"Java, Spring, Xplatform, Oracle",category:"์œ ํ†ตยท๋ฌผ๋ฅ˜"},{period:"19.01~19.06",client:"ํ•œํ™”๊ฐค๋Ÿฌ๋ฆฌ์•„",project:"๊ฐค๋Ÿฌ๋ฆฌ์•„๋ฐฑํ™”์  PDA ์ •์‚ฐ์‹œ์Šคํ…œ",role:"PM",tech:"Java, Spring, Xplatform, Oracle",category:"์œ ํ†ตยท๋ฌผ๋ฅ˜"},{period:"18.07~18.12",client:"์ด๋งˆํŠธ",project:"์ด๋งˆํŠธ ์ •์‚ฐ์‹œ์Šคํ…œ ํ”„๋กœ์ ํŠธ",role:"DA",tech:"Java, Spring, Xplatform, Oracle",category:"์œ ํ†ตยท๋ฌผ๋ฅ˜"},{period:"18.11~18.06",client:"์šฐ์ •์‚ฌ์—…์ •๋ณด์„ผํ„ฐ",project:"์šฐ์ฒด๊ตญ๊ธˆ์œต ์Šค๋งˆํŠธATM ๋„์ž…",role:"PMO",tech:"Visual C/C++, TCP/IP ์†Œ์ผ“ํ†ต์‹ ",category:"๊ณต๊ณต๊ธฐ๊ด€"},{period:"18.02~18.10",client:"ํ˜„๋Œ€๋ฐฑํ™”์ ",project:"๋ฌด์ธPOS์‹œ์Šคํ…œ ๊ตฌ์ถ•",role:"PM",tech:"Java, Spring, Xplatform, Oracle",category:"์œ ํ†ตยท๋ฌผ๋ฅ˜"},{period:"17.11~18.01",client:"KOCES",project:"KocesICPos ์ž๋™์—…๋ฐ์ดํŠธ ๋Ÿฐ์ฒ˜",role:"๊ฐœ๋ฐœ",tech:"C# .NET, Java/JSP, C/PRO*C",category:"๊ธˆ์œตยท์ œ์กฐ"},{period:"15.12~17.10",client:"LG U+",project:"LG U+ VAN ๊ณ ๋„ํ™”",role:"AA",tech:"Anylink/Tmax, WebLogic, C/PRO*C",category:"ํ†ต์‹ ยท๊ธˆ์œต"},{period:"15.07~15.11",client:"ํ•œํ™”S&C",project:"ํ•œํ™”๊ทธ๋ฃน 4์‚ฌ ํ†ตํ•ฉ HR์‹œ์Šคํ…œ",role:"PL",tech:"Java/JSP, Web Service (SOAP), IBSheet",category:"๋Œ€๊ธฐ์—…"},{period:"14.11~15.03",client:"์ฐธ์ข‹์€์—ฌํ–‰",project:"์ฝœ์„ผํ„ฐ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ•",role:"PL",tech:"ASP/.NET, Visual Studio 2012",category:"์„œ๋น„์Šค"},{period:"14.07~14.10",client:"ํ˜„๋Œ€์บํ”ผํƒˆ",project:"ํ˜„๋Œ€์บํ”ผํƒˆ ์ฐจ์„ธ๋Œ€์‹œ์Šคํ…œ",role:"PL",tech:"Java/JSP, Web Service, XPlatform",category:"๊ธˆ์œตยท์ œ์กฐ"},{period:"14.02~14.06",client:"์ค‘์†Œ๊ธฐ์—…์ฒญ",project:"์ค‘์†Œ๊ธฐ์—… 1357 ํ†ตํ•ฉ์ฝœ์„ผํ„ฐ",role:"PL",tech:"Java, Spring, XPlatform, ์ „์ž์ •๋ถ€FW",category:"๊ณต๊ณต๊ธฐ๊ด€"},{period:"13.08~13.12",client:"์‚ผ์„ฑ์ „์ž",project:"์‚ผ์„ฑ์ „์ž ํ’ˆ์งˆ๊ด€๋ฆฌ์‹œ์Šคํ…œ(QWINGS)",role:"PM",tech:"Java, Weblogic, Web Service, MiPlatform",category:"๋Œ€๊ธฐ์—…"},{period:"13.03~13.07",client:"๋Œ€์šฐ์ฆ๊ถŒ",project:"๋Œ€์šฐ์ฆ๊ถŒ ํ†ตํ•ฉ์ธํ”„๋ผ์‹œ์Šคํ…œ",role:"DBA",tech:"Java, Spring, XPlatform, Oracle",category:"ํ†ต์‹ ยท๊ธˆ์œต"},{period:"12.04~13.02",client:"์‚ผ์„ฑ์ „์ž์„œ๋น„์Šค",project:"eZone Renewal ํ”„๋กœ์ ํŠธ",role:"PL",tech:"Java, Weblogic, PRO*C, Android",category:"๋Œ€๊ธฐ์—…"},{period:"12.01~12.04",client:"๋†์ˆ˜์‚ฐ์‹ํ’ˆ์œ ํ†ต๊ณต์‚ฌ",project:"๋ฌด์—ญํ†ต๊ณ„์‹œ์Šคํ…œ ๊ตฌ์ถ•",role:"DBA",tech:"Java, Spring, Hibernate, ์ „์ž์ •๋ถ€FW",category:"๊ณต๊ณต๊ธฐ๊ด€"},{period:"11.02~11.12",client:"ํ˜„๋Œ€๋ชจ๋น„์Šค",project:"์›๊ฐ€๊ด€๋ฆฌ์‹œ์Šคํ…œ ๊ตฌ์ถ•",role:"DBA",tech:"Java, Spring, Hibernate, MiPlatform",category:"๋Œ€๊ธฐ์—…"},{period:"10.07~11.01",client:"ํ•œ๊ตญ์ „๊ธฐ์•ˆ์ „๊ณต์‚ฌ",project:"์ „๊ธฐ์•ˆ์ „ํฌํ„ธ์‹œ์Šคํ…œ ๊ตฌ์ถ•",role:"DBA",tech:"Java, Spring, Hibernate, XPlatform",category:"๊ณต๊ณต๊ธฐ๊ด€"},{period:"09.09~10.04",client:"์—ฝ์—ฐ์ดˆ์ƒ์‚ฐํ˜‘๋™์กฐํ•ฉ",project:"์—ฝ์—ฐ์ดˆ๊ฒฝ์ž‘ํ†ตํ•ฉ์‹œ์Šคํ…œ ๊ตฌ์ถ•",role:"PM",tech:"Java, Struts, i-Batis, Spring",category:"๊ณต๊ณต๊ธฐ๊ด€"},{period:"09.02~09.08",client:"ํ•œ๊ตญ์ „๊ธฐ์•ˆ์ „๊ณต์‚ฌ",project:"์•ˆ์ „์ ๊ฒ€ ๊ณ ๋„ํ™”",role:"DBA",tech:"Java, Weblogic, ์ „์ž์ •๋ถ€FW",category:"๊ณต๊ณต๊ธฐ๊ด€"},{period:"08.09~08.12",client:"๊ตญ๋ฏผ์€ํ–‰",project:"๊ตญ๋ฏผ์€ํ–‰ ์ฐจ์„ธ๋Œ€ ํฌํƒˆ",role:"PL",tech:"Java, AquaLogic, Struts/i-Batis",category:"ํ†ต์‹ ยท๊ธˆ์œต"},{period:"08.06~08.08",client:"ํ•œ๊ตญ์›์ž๋ ฅ์—ฐ๋ฃŒ",project:"์ธ์‚ฌ์ •๋ณด(HMS)์‹œ์Šคํ…œ",role:"DBA",tech:"Java/JSP/JSF, ์ž์ฒด ํ”„๋ ˆ์ž„์›Œํฌ",category:"๊ณต๊ณต๊ธฐ๊ด€"}],x=["์ „์ฒด","๊ณต๊ณต๊ธฐ๊ด€","๋Œ€๊ธฐ์—…","์œ ํ†ตยท๋ฌผ๋ฅ˜","ํ†ต์‹ ยท๊ธˆ์œต","๊ธˆ์œตยท์ œ์กฐ","๊ต์œก๊ธฐ๊ด€","IT์„œ๋น„์Šค","์„œ๋น„์Šค"],s={๊ณต๊ณต๊ธฐ๊ด€:"#0051A2",๋Œ€๊ธฐ์—…:"#7c3aed","์œ ํ†ตยท๋ฌผ๋ฅ˜":"#059669","ํ†ต์‹ ยท๊ธˆ์œต":"#d97706","๊ธˆ์œตยท์ œ์กฐ":"#dc2626",๊ต์œก๊ธฐ๊ด€:"#0891b2",IT์„œ๋น„์Šค:"#6366f1",์„œ๋น„์Šค:"#db2777"};function n(){const[c,a]=j.useState("์ „์ฒด"),l=c==="์ „์ฒด"?t:t.filter(r=>r.category===c);return e.jsxs("main",{id:"main-content",className:"inner-page",children:[e.jsx(p,{title:"๊ตฌ์ถ• ๋ ˆํผ๋Ÿฐ์Šค"}),e.jsx("section",{className:"section",children:e.jsxs("div",{className:"container",children:[e.jsxs("div",{className:"section-header",children:[e.jsx("span",{className:"section-label",children:"Reference"}),e.jsx("h2",{className:"section-title",children:"๊ตฌ์ถ• ์‹ค์ "}),e.jsxs("p",{className:"section-desc",children:["2008๋…„๋ถ€ํ„ฐ ํ˜„์žฌ๊นŒ์ง€ ๊ตญ๋‚ด ์ฃผ์š” ๊ธฐ๊ด€ยท๊ธฐ์—… ",t.length,"๊ฐœ ํ”„๋กœ์ ํŠธ ์„ฑ๊ณต ์ˆ˜ํ–‰"]})]}),e.jsx("div",{className:"grid-4",style:{marginBottom:"40px"},children:[{val:`${t.length}+`,label:"์ด ํ”„๋กœ์ ํŠธ",color:"var(--primary)"},{val:"20+",label:"๋…„ ๊ฒฝ๋ ฅ",color:"var(--accent)"},{val:"15+",label:"๊ณต๊ณต๊ธฐ๊ด€",color:"#7c3aed"},{val:"10+",label:"๋Œ€๊ธฐ์—…ยท๊ธˆ์œต",color:"#059669"}].map((r,o)=>e.jsxs("div",{className:"card",style:{padding:"24px",textAlign:"center"},children:[e.jsx("div",{style:{fontSize:"32px",fontWeight:"900",color:r.color},children:r.val}),e.jsx("p",{style:{fontSize:"13px",color:"var(--gray-500)",marginTop:"6px"},children:r.label})]},o))}),e.jsx("div",{className:"ref-filters",children:x.map(r=>e.jsx("button",{className:`ref-filter-btn ${c===r?"active":""}`,onClick:()=>a(r),children:r},r))}),e.jsx("div",{className:"ref-table-wrap",children:e.jsxs("table",{className:"ref-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"๊ธฐ๊ฐ„"}),e.jsx("th",{children:"๊ณ ๊ฐ์‚ฌ"}),e.jsx("th",{children:"ํ”„๋กœ์ ํŠธ๋ช…"}),e.jsx("th",{children:"์—ญํ• "}),e.jsx("th",{children:"์ฃผ์š”๊ธฐ์ˆ "}),e.jsx("th",{children:"๋ถ„์•ผ"})]})}),e.jsx("tbody",{children:l.map((r,o)=>e.jsxs("tr",{children:[e.jsx("td",{className:"ref-period",children:r.period}),e.jsx("td",{className:"ref-client",children:r.client}),e.jsx("td",{className:"ref-project",children:r.project}),e.jsx("td",{children:e.jsx("span",{className:"ref-role",children:r.role})}),e.jsx("td",{className:"ref-tech",children:r.tech}),e.jsx("td",{children:e.jsx("span",{className:"ref-cat-badge",style:{background:s[r.category]+"18",color:s[r.category]},children:r.category})})]},o))})]})})]})})]})}const S=[{name:"Oracle",logo:"๐Ÿ”ด",desc:"Oracle DB ๊ณต์‹ ํŒŒํŠธ๋„ˆ โ€” Oracle 19c ์ „๋ฌธ DBA ์ธ์ฆ",tier:"Gold"},{name:"Red Hat",logo:"๐ŸŽฉ",desc:"RHELยทOpenShift ํŒŒํŠธ๋„ˆ โ€” ๋ฆฌ๋ˆ…์Šค ์ธํ”„๋ผ ๊ตฌ์ถ•",tier:"Silver"},{name:"JEUS (TmaxSoft)",logo:"โš™๏ธ",desc:"๊ตญ์‚ฐ WAS JEUS/Tmax ๊ณต์‹ ํŒŒํŠธ๋„ˆ",tier:"Gold"},{name:"Tibero",logo:"๐Ÿ—„๏ธ",desc:"Tibero DBMS ๊ณต์‹ ํŒŒํŠธ๋„ˆ โ€” ๊ณต๊ณต๊ธฐ๊ด€ DB ์ „ํ™˜",tier:"Gold"},{name:"Samsung SDS",logo:"๐Ÿ’ผ",desc:"์‚ผ์„ฑSDS ํ˜‘๋ ฅ์‚ฌ โ€” ์‚ผ์„ฑ์ „์ž CRM/ํ’ˆ์งˆ ์‹œ์Šคํ…œ ๊ณต๋™ ์ˆ˜ํ–‰",tier:"Partner"},{name:"Nexacro",logo:"๐Ÿ–ฅ๏ธ",desc:"ํˆฌ๋น„์†Œํ”„ํŠธ Nexacro ๊ณต์‹ ํŒŒํŠธ๋„ˆ โ€” UI ๊ฐœ๋ฐœ ์ „๋ฌธ",tier:"Silver"},{name:"OZ Report",logo:"๐Ÿ“Š",desc:"OZ e-Form ๊ณต์‹ ํŒŒํŠธ๋„ˆ โ€” ๊ณต๊ณต ์ „์ž๋ฌธ์„œ ์†”๋ฃจ์…˜",tier:"Silver"},{name:"Ollama",logo:"๐Ÿค–",desc:"์˜จํ”„๋ ˆ๋ฏธ์Šค LLM ํŒŒํŠธ๋„ˆ โ€” GUARDiA AI ์—”์ง„ ๊ณต๊ธ‰์‚ฌ",tier:"Tech"}],d={Gold:"#d97706",Silver:"#6b7280",Partner:"#0051A2",Tech:"#7c3aed"};function v(){return e.jsxs("main",{id:"main-content",className:"inner-page",children:[e.jsx(p,{title:"ํŒŒํŠธ๋„ˆ"}),e.jsx("section",{className:"section",children:e.jsxs("div",{className:"container",children:[e.jsxs("div",{className:"section-header",children:[e.jsx("span",{className:"section-label",children:"Partners"}),e.jsx("h2",{className:"section-title",children:"๊ธฐ์ˆ  ํŒŒํŠธ๋„ˆ"}),e.jsx("p",{className:"section-desc",children:"์ตœ๊ณ ์˜ ๊ธฐ์ˆ  ํŒŒํŠธ๋„ˆ์™€ ํ•จ๊ป˜ ์ตœ์„ ์˜ ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค"})]}),e.jsx("div",{className:"grid-4",children:S.map((c,a)=>e.jsxs("div",{className:"card partner-card",children:[e.jsx("div",{className:"partner-logo",children:c.logo}),e.jsxs("div",{className:"partner-tier",style:{background:d[c.tier]+"1a",color:d[c.tier]},children:[c.tier," Partner"]}),e.jsx("h3",{className:"partner-name",children:c.name}),e.jsx("p",{className:"partner-desc",children:c.desc})]},a))}),e.jsxs("div",{className:"partner-cta",children:[e.jsx("h3",{children:"ํŒŒํŠธ๋„ˆ์‹ญ ๋ฌธ์˜"}),e.jsx("p",{children:"์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ๊ณผ ๊ธฐ์ˆ  ํŒŒํŠธ๋„ˆ์‹ญ์„ ๋งบ๊ณ  ์‹ถ์œผ์‹  ๊ธฐ์—…์€ ์•„๋ž˜๋กœ ์—ฐ๋ฝ ์ฃผ์‹ญ์‹œ์˜ค."}),e.jsx("a",{href:"/support/contact?type=์‚ฌ์—…์ œ์•ˆ",className:"btn btn-primary btn-lg",children:"ํŒŒํŠธ๋„ˆ์‹ญ ์ œ์•ˆํ•˜๊ธฐ"})]})]})})]})}function y(){return e.jsxs(h,{children:[e.jsx(i,{path:"reference",element:e.jsx(n,{})}),e.jsx(i,{path:"partner",element:e.jsx(v,{})}),e.jsx(i,{path:"*",element:e.jsx(n,{})})]})}export{y as default}; diff --git a/backend/src/main/resources/static/assets/Business-EM7OeA4c.css b/backend/src/main/resources/static/assets/Business-EM7OeA4c.css new file mode 100644 index 0000000..67e9340 --- /dev/null +++ b/backend/src/main/resources/static/assets/Business-EM7OeA4c.css @@ -0,0 +1 @@ +.ref-filters{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:24px}.ref-filter-btn{padding:7px 18px;border-radius:20px;border:1px solid var(--gray-200);font-size:13px;font-weight:500;color:var(--gray-600);cursor:pointer;transition:all var(--fast) var(--ease);background:var(--white)}.ref-filter-btn:hover{border-color:var(--primary);color:var(--primary)}.ref-filter-btn.active{background:var(--primary);border-color:var(--primary);color:#fff}.ref-table-wrap{overflow-x:auto;border-radius:12px;border:1px solid var(--gray-200)}.ref-table{width:100%;border-collapse:collapse;min-width:800px}.ref-table th{background:var(--secondary);color:#fffc;padding:14px 16px;text-align:left;font-size:12px;font-weight:600;letter-spacing:.5px}.ref-table td{padding:13px 16px;font-size:13px;border-bottom:1px solid var(--gray-100);vertical-align:middle}.ref-table tr:last-child td{border-bottom:none}.ref-table tr:hover td{background:var(--gray-50)}.ref-period{color:var(--gray-500);font-size:12px;white-space:nowrap}.ref-client{font-weight:700;color:var(--gray-800);white-space:nowrap}.ref-project{color:var(--gray-700)}.ref-role{padding:3px 10px;border-radius:12px;font-size:11px;font-weight:700;background:var(--primary-light);color:var(--primary);white-space:nowrap}.ref-tech{font-size:12px;color:var(--gray-500)}.ref-cat-badge{padding:3px 10px;border-radius:12px;font-size:11px;font-weight:600;white-space:nowrap}.partner-card{padding:32px 24px;text-align:center}.partner-logo{font-size:48px;margin-bottom:12px}.partner-tier{display:inline-block;padding:3px 12px;border-radius:12px;font-size:11px;font-weight:700;margin-bottom:12px}.partner-name{font-size:16px;font-weight:700;color:var(--gray-900);margin-bottom:10px}.partner-desc{font-size:13px;color:var(--gray-600);line-height:1.6}.partner-cta{margin-top:64px;text-align:center;padding:56px;background:linear-gradient(135deg,var(--primary-light),rgba(0,163,224,.08));border-radius:16px;border:1px solid var(--gray-200)}.partner-cta h3{font-size:24px;font-weight:800;margin-bottom:12px}.partner-cta p{color:var(--gray-600);margin-bottom:24px;font-size:15px} diff --git a/backend/src/main/resources/static/assets/Common-BmvLh5lB.css b/backend/src/main/resources/static/assets/Common-BmvLh5lB.css new file mode 100644 index 0000000..36fe3cb --- /dev/null +++ b/backend/src/main/resources/static/assets/Common-BmvLh5lB.css @@ -0,0 +1 @@ +.inner-page{padding-top:var(--header-h)}.page-hero{background:linear-gradient(135deg,var(--secondary),var(--primary-dark));padding:60px 0;color:#fff}.page-hero-title{font-size:40px;font-weight:900;margin:8px 0 12px}.page-hero p{color:#ffffffbf;font-size:16px} diff --git a/backend/src/main/resources/static/assets/Company-BOdWAIQ4.js b/backend/src/main/resources/static/assets/Company-BOdWAIQ4.js new file mode 100644 index 0000000..aaf3222 --- /dev/null +++ b/backend/src/main/resources/static/assets/Company-BOdWAIQ4.js @@ -0,0 +1 @@ +import{j as s,b as t,a as l,N as d}from"./index-ChpGil2q.js";/* empty css */const o=[{path:"/company/greeting",label:"CEO ์ธ์‚ฌ๋ง"},{path:"/company/history",label:"์—ฐํ˜"},{path:"/company/organization",label:"์กฐ์ง๋„"},{path:"/company/ci",label:"CI ์†Œ๊ฐœ"},{path:"/company/location",label:"์˜ค์‹œ๋Š” ๊ธธ"}];function n({title:e}){return s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"page-hero",children:s.jsxs("div",{className:"container",children:[s.jsx("span",{className:"section-label",children:"Company"}),s.jsx("h1",{className:"page-hero-title",children:e})]})}),s.jsx("nav",{className:"sub-nav",children:s.jsx("div",{className:"container",children:o.map(a=>s.jsx(d,{to:a.path,className:({isActive:i})=>"sub-nav-item"+(i?" active":""),children:a.label},a.path))})})]})}function r(){return s.jsxs("main",{id:"main-content",className:"inner-page",children:[s.jsx(n,{title:"CEO ์ธ์‚ฌ๋ง"}),s.jsx("section",{className:"section",children:s.jsxs("div",{className:"container",style:{maxWidth:"860px"},children:[s.jsxs("div",{className:"ceo-wrap",children:[s.jsxs("div",{className:"ceo-photo",children:[s.jsx("div",{className:"ceo-avatar",children:s.jsx("span",{children:"CEO"})}),s.jsx("p",{className:"ceo-name",children:"๋Œ€ํ‘œ์ด์‚ฌ"}),s.jsx("p",{className:"ceo-sign",children:"(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ "})]}),s.jsxs("div",{className:"ceo-text",children:[s.jsxs("h2",{children:["์•ˆ๋…•ํ•˜์‹ญ๋‹ˆ๊นŒ,",s.jsx("br",{}),"(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ๋Œ€ํ‘œ์ด์‚ฌ์ž…๋‹ˆ๋‹ค."]}),s.jsx("div",{className:"divider divider-left",style:{margin:"20px 0 28px"}}),["์ €ํฌ (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ์€ 2000๋…„ ์ฐฝ๋ฆฝ ์ด๋ž˜ 20๋…„ ์ด์ƒ ๊ณต๊ณต๊ธฐ๊ด€ ๋ฐ ๋Œ€๊ธฐ์—… IT ์ „๋ฌธ ์„œ๋น„์Šค ๊ธฐ์—…์œผ๋กœ ์„ฑ์žฅํ•ด ์™”์Šต๋‹ˆ๋‹ค. ์‚ผ์„ฑ์ „์ž, ํ˜„๋Œ€๋ฐฑํ™”์ , ๊ตญ๋ฏผ์—ฐ๊ธˆ, ํ—Œ๋ฒ•์žฌํŒ์†Œ ๋“ฑ ๊ตญ๋‚ด ์ฃผ์š” ๊ธฐ๊ด€ยท๊ธฐ์—…์˜ ํ•ต์‹ฌ ์‹œ์Šคํ…œ์„ ์„ฑ๊ณต์ ์œผ๋กœ ๊ตฌ์ถ•ยท์šด์˜ํ•œ ํ’๋ถ€ํ•œ ๊ฒฝํ—˜์„ ๋ณด์œ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.",'์ตœ๊ทผ์—๋Š” GUARDiA ITSM ํ”Œ๋žซํผ์„ ํ†ตํ•ด "AI ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ ์ž์œจ ์šด์˜"์ด๋ผ๋Š” ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„์„ ์ œ์‹œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฉ”์‹ ์ € ํ•œ ์ค„ ๋ช…๋ น์œผ๋กœ 1,000๊ฐœ ์ด์ƒ์˜ ๊ด€๊ณต์„œ ๋ ˆ๊ฑฐ์‹œ ์ธํ”„๋ผ๋ฅผ ์ž๋™ํ™”ํ•˜๋Š” ํ˜์‹ ์ ์ธ ์†”๋ฃจ์…˜์œผ๋กœ, ๋Œ€์ƒ ์„œ๋ฒ„์— ๋ณ„๋„ ์†Œํ”„ํŠธ์›จ์–ด ์„ค์น˜ ์—†์ด ํ‘œ์ค€ SSH/SFTP ํ”„๋กœํ† ์ฝœ๋งŒ์œผ๋กœ ์šด์˜ ์ž๋™ํ™”๋ฅผ ์‹คํ˜„ํ•ฉ๋‹ˆ๋‹ค.',"์•ž์œผ๋กœ๋„ ๊ณ ๊ฐ์˜ ์„ฑ๊ณต์ด ๊ณง ์ €ํฌ์˜ ์„ฑ๊ณต์ด๋ผ๋Š” ์‹ ๋… ์•„๋ž˜, ์ตœ๊ณ ์˜ ๊ธฐ์ˆ ๋ ฅ๊ณผ ์„œ๋น„์Šค๋กœ ๋ณด๋‹ตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์–ธ์ œ๋‚˜ ์—ฌ๋Ÿฌ๋ถ„ ๊ณ์—์„œ ๋””์ง€ํ„ธ ํ˜์‹ ์˜ ํŒŒํŠธ๋„ˆ๊ฐ€ ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค.","๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค."].map((e,a)=>s.jsx("p",{className:"ceo-para",children:e},a))]})]}),s.jsxs("div",{style:{marginTop:"72px"},children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Core Values"}),s.jsx("h2",{className:"section-title",children:"ํ•ต์‹ฌ ๊ฐ€์น˜"})]}),s.jsx("div",{className:"grid-4",children:[{icon:"๐ŸŽฏ",title:"๊ณ ๊ฐ ์ค‘์‹ฌ",desc:"๊ณ ๊ฐ์˜ ์„ฑ๊ณต์„ ์ตœ์šฐ์„ ์œผ๋กœ ์ƒ๊ฐํ•˜๋ฉฐ ์ตœ์ ์˜ ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค"},{icon:"๐Ÿš€",title:"๊ธฐ์ˆ  ํ˜์‹ ",desc:"AIยทํด๋ผ์šฐ๋“œ ์ตœ์‹  ๊ธฐ์ˆ ๋กœ ๊ณ ๊ฐ์˜ ๋””์ง€ํ„ธ ์ „ํ™˜์„ ์„ ๋„ํ•ฉ๋‹ˆ๋‹ค"},{icon:"๐Ÿค",title:"์‹ ๋ขฐ์™€ ์ฑ…์ž„",desc:"20๋…„ ์ด์ƒ ์ถ•์ ๋œ ์‹ ๋ขฐ๋กœ ์ฑ…์ž„๊ฐ ์žˆ๋Š” ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค"},{icon:"๐ŸŒฑ",title:"์ง€์† ์„ฑ์žฅ",desc:"๊ตฌ์„ฑ์›๊ณผ ๊ณ ๊ฐ์ด ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ์ง€์† ๊ฐ€๋Šฅํ•œ ํŒŒํŠธ๋„ˆ์‹ญ์„ ์ถ”๊ตฌํ•ฉ๋‹ˆ๋‹ค"}].map((e,a)=>s.jsxs("div",{className:"card",style:{padding:"32px 24px",textAlign:"center"},children:[s.jsx("div",{style:{fontSize:"40px",marginBottom:"16px"},children:e.icon}),s.jsx("h3",{style:{fontSize:"18px",fontWeight:"700",marginBottom:"12px"},children:e.title}),s.jsx("p",{style:{fontSize:"14px",color:"var(--gray-600)",lineHeight:"1.7"},children:e.desc})]},a))})]})]})})]})}const m=[{year:"2026",items:["GUARDiA ITSM v2.0 ์ถœ์‹œ โ€” AI ChatOps ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ํ”Œ๋žซํผ","GS์ธ์ฆ 1๋“ฑ๊ธ‰ ์‹ ์ฒญ ์ค€๋น„ ์™„๋ฃŒ (TTA ์‹ฌ์‚ฌ ์˜ˆ์ •)","๊ณต๊ณต๊ธฐ๊ด€ 1,000๊ฐœ ์ด์ƒ ๋ฉ€ํ‹ฐํ…Œ๋„ŒํŠธ ์ง€์› ๋ชฉํ‘œ ๋‹ฌ์„ฑ"]},{year:"2025",items:["์‚ผ์„ฑ์ „์ž ์ฐจ์„ธ๋Œ€ CRM ๊ตฌ์ถ• (DB Migration / DA / ํŠœ๋‹)","GUARDiA ITSM v1.0 ๋ฒ ํƒ€ ์„œ๋น„์Šค ๊ฐœ์‹œ","AI ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ ์ž๋™ํ™” ํŠนํ—ˆ ์ถœ์›"]},{year:"2024",items:["DELL ์ฐจ์„ธ๋Œ€ CRM ๊ตฌ์ถ• โ€” DBA ์—ญํ•  ์ˆ˜ํ–‰ (์— ๋กœ)","์†Œ์ƒ๊ณต์ธ์ปจ์„คํŒ…์‹œ์Šคํ…œ ๊ตฌ์ถ• (์„œ์šธ์‹ ์šฉ๋ณด์ฆ์žฌ๋‹จ, PM)","๊ตญ๋ฏผ์—ฐ๊ธˆ ์ฐจ์„ธ๋Œ€ ์‹œ์Šคํ…œ ๊ตฌ์ถ• (AA)"]},{year:"2023",items:["ํ—Œ๋ฒ•์žฌํŒ์†Œ ํฌํ„ธ์‹œ์Šคํ…œ ๊ตฌ์ถ• (PM)","์„œ์šธ์‹ ์šฉ๋ณด์ฆ์žฌ๋‹จ ๋ชจ๋ฐ”์ผ์•ฑ ๊ตฌ์ถ• ์™„๋ฃŒ (PM)"]},{year:"2022",items:["์—์ดํ…์—์ดํ”ผ ํ†ตํ•ฉ์œ ์ง€๋ณด์ˆ˜๊ด€๋ฆฌ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ (PM)","ํ—Œ๋ฒ•์žฌํŒ์†Œ ํ†ตํ•ฉ๋ณด์•ˆ๊ด€์ œ์‹œ์Šคํ…œ ๊ตฌ์ถ• (PM)"]},{year:"2020โ€“2021",items:["ํ˜„๋Œ€๋ฐฑํ™”์  HKOS ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ/๊ตฌ์ถ• (PM)","์„œ์šธ์‹œ๋ฆฝ๋Œ€ ๋Œ€ํ•™ํ–‰์ •์ •๋ณด์‹œ์Šคํ…œ ์„ฑ๋Šฅ ๊ฐœ์„  (PL)","๋†ํ˜‘ ํ•˜๋‚˜๋กœ๋งˆํŠธ ESL ์‹œ์Šคํ…œ ๊ตฌ์ถ• (PM)"]},{year:"2018โ€“2019",items:["์ด๋งˆํŠธ ์ •์‚ฐ์‹œ์Šคํ…œ ํ”„๋กœ์ ํŠธ (DA)","์šฐ์ฒด๊ตญ๊ธˆ์œต ์Šค๋งˆํŠธATM ๋„์ž… (PMO)","ํ˜„๋Œ€๋ฐฑํ™”์  ๋ฌด์ธPOS์‹œ์Šคํ…œ ๊ตฌ์ถ• (PM)","๊ฐค๋Ÿฌ๋ฆฌ์•„๋ฐฑํ™”์  PDA ์ •์‚ฐ์‹œ์Šคํ…œ (PM)"]},{year:"2015โ€“2017",items:["LG U+ VAN ๊ณ ๋„ํ™” โ€” ์Šน์ธ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ FEP/AP/BEP (AA)","ํ•œํ™”๊ทธ๋ฃน 4์‚ฌ ํ†ตํ•ฉ HR์‹œ์Šคํ…œ ๊ตฌ์ถ• (PL)","์ฐธ์ข‹์€์—ฌํ–‰ ์ฝœ์„ผํ„ฐ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ• (PL)"]},{year:"2013โ€“2014",items:["์‚ผ์„ฑ์ „์ž ํ’ˆ์งˆ๊ด€๋ฆฌ์‹œ์Šคํ…œ(QWINGS) ๊ตฌ์ถ• (PM)","๋Œ€์šฐ์ฆ๊ถŒ ํ†ตํ•ฉ์ธํ”„๋ผ์‹œ์Šคํ…œ (DBA)","ํ˜„๋Œ€์บํ”ผํƒˆ ์ฐจ์„ธ๋Œ€์‹œ์Šคํ…œ (PL)","์ค‘์†Œ๊ธฐ์—… 1357 ํ†ตํ•ฉ์ฝœ์„ผํ„ฐ ๊ตฌ์ถ• (PL)"]},{year:"2010โ€“2012",items:["์‚ผ์„ฑ์ „์ž์„œ๋น„์Šค eZone ๊ฐฑ์‹  (PL)","ํ˜„๋Œ€๋ชจ๋น„์Šค ์›๊ฐ€๊ด€๋ฆฌ์‹œ์Šคํ…œ (DBA)","ํ•œ๊ตญ์ „๊ธฐ์•ˆ์ „๊ณต์‚ฌ ์ „๊ธฐ์•ˆ์ „ํฌํ„ธ์‹œ์Šคํ…œ (DBA)"]},{year:"2008โ€“2009",items:["๊ตญ๋ฏผ์€ํ–‰ ์ฐจ์„ธ๋Œ€ ํฌํƒˆ ๊ตฌ์ถ• (PL)","ํ•œ๊ตญ์›์ž๋ ฅ์—ฐ๋ฃŒ ์ธ์‚ฌ์ •๋ณด(HMS)์‹œ์Šคํ…œ (DBA)","ํ•œ๊ตญ์ „๊ธฐ์•ˆ์ „๊ณต์‚ฌ ์•ˆ์ „์ ๊ฒ€ ๊ณ ๋„ํ™” (DBA)"]},{year:"2000",items:["(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ์ฐฝ๋ฆฝ","๊ณต๊ณต๊ธฐ๊ด€ IT ์ธํ”„๋ผ ์„œ๋น„์Šค ๊ฐœ์‹œ"]}];function x(){return s.jsxs("main",{id:"main-content",className:"inner-page",children:[s.jsx(n,{title:"์—ฐํ˜"}),s.jsx("section",{className:"section",children:s.jsxs("div",{className:"container",style:{maxWidth:"900px"},children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"History"}),s.jsx("h2",{className:"section-title",children:"20๋…„+ ์„ฑ์žฅ์˜ ์—ญ์‚ฌ"}),s.jsx("p",{className:"section-desc",children:"2000๋…„ ์ฐฝ๋ฆฝ ์ด๋ž˜ ๊ตญ๋‚ด ์ฃผ์š” ๊ธฐ๊ด€ยท๊ธฐ์—…๊ณผ ํ•จ๊ป˜ ์„ฑ์žฅํ•ด ์™”์Šต๋‹ˆ๋‹ค"})]}),s.jsx("div",{className:"timeline",children:m.map((e,a)=>s.jsxs("div",{className:"timeline-row",children:[s.jsx("div",{className:"timeline-year",children:e.year}),s.jsx("div",{className:"timeline-dot"}),s.jsx("div",{className:"timeline-content",children:e.items.map((i,c)=>s.jsxs("div",{className:"timeline-item",children:[s.jsx("span",{className:"timeline-bullet"}),i]},c))})]},a))})]})})]})}function h(){return s.jsxs("main",{id:"main-content",className:"inner-page",children:[s.jsx(n,{title:"์กฐ์ง๋„"}),s.jsx("section",{className:"section",children:s.jsxs("div",{className:"container",style:{maxWidth:"960px"},children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Organization"}),s.jsx("h2",{className:"section-title",children:"์กฐ์ง ๊ตฌ์„ฑ"})]}),s.jsxs("div",{className:"org-chart",children:[s.jsx("div",{className:"org-top",children:s.jsx("div",{className:"org-box ceo",children:"๋Œ€ํ‘œ์ด์‚ฌ"})}),s.jsx("div",{className:"org-line-v"}),s.jsx("div",{className:"org-depts",children:[{name:"๊ฐœ๋ฐœ๋ณธ๋ถ€",color:"var(--primary)",teams:["AI๊ฐœ๋ฐœํŒ€","ํ”Œ๋žซํผ๊ฐœ๋ฐœํŒ€","ํ”„๋ก ํŠธ์—”๋“œํŒ€"]},{name:"๊ธฐ์ˆ ๋ณธ๋ถ€",color:"var(--accent)",teams:["์ธํ”„๋ผํŒ€","DBAํŒ€","๋ณด์•ˆํŒ€"]},{name:"PM๋ณธ๋ถ€",color:"#7c3aed",teams:["SI์‚ฌ์—…ํŒ€","SM์šด์˜ํŒ€","PMOํŒ€"]},{name:"๊ฒฝ์˜์ง€์›๋ณธ๋ถ€",color:"#059669",teams:["๊ฒฝ์˜๊ธฐํšํŒ€","์˜์—…ํŒ€","์ธ์‚ฌยท์ด๋ฌดํŒ€"]}].map((e,a)=>s.jsxs("div",{className:"org-dept-col",children:[s.jsx("div",{className:"org-line-h"}),s.jsx("div",{className:"org-line-v-short"}),s.jsx("div",{className:"org-box dept",style:{borderColor:e.color,color:e.color},children:e.name}),s.jsx("div",{className:"org-teams",children:e.teams.map((i,c)=>s.jsx("div",{className:"org-box team",children:i},c))})]},a))})]}),s.jsx("div",{className:"grid-4",style:{marginTop:"64px"},children:[{label:"์ „์ฒด ์ž„์ง์›",value:"50+",unit:"๋ช…"},{label:"๊ฐœ๋ฐœ ์ธ๋ ฅ",value:"70",unit:"%"},{label:"ํ‰๊ท  ๊ฒฝ๋ ฅ",value:"8",unit:"๋…„+"},{label:"๊ตญ๊ฐ€ ๊ณต์ธ ์ž๊ฒฉ",value:"30+",unit:"๊ฐœ"}].map((e,a)=>s.jsxs("div",{className:"card",style:{padding:"32px",textAlign:"center"},children:[s.jsxs("div",{style:{fontSize:"36px",fontWeight:"900",color:"var(--primary)"},children:[e.value,s.jsx("span",{style:{fontSize:"18px"},children:e.unit})]}),s.jsx("p",{style:{marginTop:"8px",color:"var(--gray-600)",fontSize:"14px"},children:e.label})]},a))})]})})]})}function j(){return s.jsxs("main",{id:"main-content",className:"inner-page",children:[s.jsx(n,{title:"CI ์†Œ๊ฐœ"}),s.jsx("section",{className:"section",children:s.jsxs("div",{className:"container",style:{maxWidth:"900px"},children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Corporate Identity"}),s.jsx("h2",{className:"section-title",children:"๋ธŒ๋žœ๋“œ ์•„์ด๋ดํ‹ฐํ‹ฐ"}),s.jsx("p",{className:"section-desc",children:"์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ์˜ ๋ธŒ๋žœ๋“œ๋Š” ์‹ ๋ขฐยทํ˜์‹ ยท์ „๋ฌธ์„ฑ์„ ์ƒ์ง•ํ•ฉ๋‹ˆ๋‹ค"})]}),s.jsxs("div",{className:"ci-section",children:[s.jsx("h3",{className:"ci-title",children:"๋กœ๊ณ  ์‹œ์Šคํ…œ"}),s.jsxs("div",{className:"ci-logo-wrap",children:[s.jsxs("div",{className:"ci-logo-box dark",children:[s.jsxs("div",{className:"ci-logo-text-wrap",children:[s.jsx("span",{className:"ci-logo-mark",children:"GEO"}),s.jsx("span",{className:"ci-logo-sub",children:"์ •๋ณด๊ธฐ์ˆ "})]}),s.jsx("p",{className:"ci-logo-label",children:"๋‹คํฌ ๋ฐฐ๊ฒฝ์šฉ"})]}),s.jsxs("div",{className:"ci-logo-box light",children:[s.jsxs("div",{className:"ci-logo-text-wrap",children:[s.jsx("span",{className:"ci-logo-mark",style:{color:"var(--primary)"},children:"GEO"}),s.jsx("span",{className:"ci-logo-sub",style:{color:"var(--gray-700)"},children:"์ •๋ณด๊ธฐ์ˆ "})]}),s.jsx("p",{className:"ci-logo-label",style:{color:"var(--gray-500)"},children:"๋ผ์ดํŠธ ๋ฐฐ๊ฒฝ์šฉ"})]})]})]}),s.jsxs("div",{className:"ci-section",children:[s.jsx("h3",{className:"ci-title",children:"๋ธŒ๋žœ๋“œ ์ปฌ๋Ÿฌ"}),s.jsx("div",{className:"ci-colors",children:[{name:"Primary Blue",hex:"#0051A2",cmyk:"C100 M50 Y0 K37",usage:"์ฃผ์š” UIยท๋ฒ„ํŠผยท๊ฐ•์กฐ"},{name:"Accent Blue",hex:"#00A3E0",cmyk:"C100 M28 Y0 K12",usage:"ํฌ์ธํŠธยท๋งํฌยท์•„์ด์ฝ˜"},{name:"Dark Navy",hex:"#1A1A2E",cmyk:"C40 M40 Y0 K82",usage:"ํ—ค๋”ยท๋ฐฐ๊ฒฝยทํ…์ŠคํŠธ"},{name:"Pure White",hex:"#FFFFFF",cmyk:"C0 M0 Y0 K0",usage:"๋ฐฐ๊ฒฝยท๋ฐ˜์ „ ํ…์ŠคํŠธ"}].map((e,a)=>s.jsxs("div",{className:"ci-color-card",children:[s.jsx("div",{className:"ci-color-swatch",style:{background:e.hex,border:e.hex==="#FFFFFF"?"1px solid #eee":"none"}}),s.jsxs("div",{className:"ci-color-info",children:[s.jsx("strong",{children:e.name}),s.jsx("span",{className:"ci-hex",children:e.hex}),s.jsx("span",{className:"ci-cmyk",children:e.cmyk}),s.jsx("span",{className:"ci-usage",children:e.usage})]})]},a))})]}),s.jsxs("div",{className:"ci-section",children:[s.jsx("h3",{className:"ci-title",children:"๋ธŒ๋žœ๋“œ ์Šฌ๋กœ๊ฑด"}),s.jsxs("div",{className:"ci-slogan-wrap",children:[s.jsx("p",{className:"ci-slogan-main",children:'"๊ณต๊ณต๊ธฐ๊ด€ IT, ์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ๊ณผ ํ•จ๊ป˜"'}),s.jsx("p",{className:"ci-slogan-sub",children:"20๋…„ ์‹ ๋ขฐ์˜ ๊ธฐ์ˆ ๋ ฅ์œผ๋กœ AI ์ธํ”„๋ผ ํ˜์‹ ์„ ์ด๋•๋‹ˆ๋‹ค"})]})]})]})})]})}function p(){return s.jsxs("main",{id:"main-content",className:"inner-page",children:[s.jsx(n,{title:"์˜ค์‹œ๋Š” ๊ธธ"}),s.jsx("section",{className:"section",children:s.jsxs("div",{className:"container",style:{maxWidth:"960px"},children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Location"}),s.jsx("h2",{className:"section-title",children:"์˜ค์‹œ๋Š” ๊ธธ"})]}),s.jsx("div",{className:"map-wrap",children:s.jsxs("div",{className:"map-placeholder",children:[s.jsx("div",{className:"map-pin",children:"๐Ÿ“"}),s.jsx("p",{children:"(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ "}),s.jsx("p",{style:{fontSize:"13px",color:"var(--gray-400)",marginTop:"8px"},children:"์„œ์šธํŠน๋ณ„์‹œ ๊ฐ•์„œ๊ตฌ ์–‘์ฒœ๋กœ 570"})]})}),s.jsxs("div",{className:"location-grid",children:[s.jsxs("div",{className:"card",style:{padding:"36px"},children:[s.jsx("h3",{className:"loc-office-title",children:"๐Ÿ“ ๋ณธ์‚ฌ"}),s.jsx("table",{className:"loc-table",children:s.jsxs("tbody",{children:[s.jsxs("tr",{children:[s.jsx("th",{children:"์ฃผ์†Œ"}),s.jsxs("td",{children:["์„œ์šธํŠน๋ณ„์‹œ ๊ฐ•์„œ๊ตฌ ์–‘์ฒœ๋กœ 570",s.jsx("br",{}),"NH์„œ์šธํƒ€์›Œ 5์ธต"]})]}),s.jsxs("tr",{children:[s.jsx("th",{children:"๋Œ€ํ‘œ์ „ํ™”"}),s.jsx("td",{children:"02-784-9271"})]}),s.jsxs("tr",{children:[s.jsx("th",{children:"ํŒฉ์Šค"}),s.jsx("td",{children:"02-784-9272"})]}),s.jsxs("tr",{children:[s.jsx("th",{children:"์ด๋ฉ”์ผ"}),s.jsx("td",{children:"info@zioinfo.co.kr"})]}),s.jsxs("tr",{children:[s.jsx("th",{children:"์šด์˜์‹œ๊ฐ„"}),s.jsx("td",{children:"ํ‰์ผ 09:00 ~ 18:00 (์ ์‹ฌ 12:00~13:00)"})]})]})})]}),s.jsxs("div",{className:"card",style:{padding:"36px"},children:[s.jsx("h3",{className:"loc-office-title",children:"๐Ÿš‡ ๊ตํ†ต ์•ˆ๋‚ด"}),s.jsx("div",{className:"transport-list",children:[{type:"์ง€ํ•˜์ฒ ",icon:"๐Ÿš‡",lines:["5ํ˜ธ์„  ๋ฐœ์‚ฐ์—ญ 1๋ฒˆ ์ถœ๊ตฌ ๋„๋ณด 5๋ถ„","9ํ˜ธ์„  ๋งˆ๊ณก๋‚˜๋ฃจ์—ญ 3๋ฒˆ ์ถœ๊ตฌ ๋„๋ณด 10๋ถ„"]},{type:"๋ฒ„์Šค",icon:"๐ŸšŒ",lines:["๊ฐ•์„œ05, ๊ฐ•์„œ06 โ€” ๊ฐ•์„œ๊ตฌ์ฒญ ์ •๋ฅ˜์žฅ ํ•˜์ฐจ","60, 62, 604 โ€” ๋ฐœ์‚ฐ์—ญ ํ•˜์ฐจ"]},{type:"์ž๊ฐ€์šฉ",icon:"๐Ÿš—",lines:["์˜ฌ๋ฆผํ”ฝ๋Œ€๋กœ โ†’ ๊ฐ•์„œIC โ†’ ์–‘์ฒœ๋กœ ๋ฐฉ๋ฉด","์ฃผ์ฐจ ๊ฐ€๋Šฅ (๋ฐฉ๋ฌธ ์ „ ์‚ฌ์ „ ์—ฐ๋ฝ ๊ถŒ์žฅ)"]}].map((e,a)=>s.jsxs("div",{className:"transport-item",children:[s.jsxs("div",{className:"transport-header",children:[s.jsx("span",{children:e.icon}),s.jsx("strong",{children:e.type})]}),e.lines.map((i,c)=>s.jsx("p",{children:i},c))]},a))})]})]})]})})]})}function g(){return s.jsxs(t,{children:[s.jsx(l,{path:"greeting",element:s.jsx(r,{})}),s.jsx(l,{path:"history",element:s.jsx(x,{})}),s.jsx(l,{path:"organization",element:s.jsx(h,{})}),s.jsx(l,{path:"ci",element:s.jsx(j,{})}),s.jsx(l,{path:"location",element:s.jsx(p,{})}),s.jsx(l,{path:"*",element:s.jsx(r,{})})]})}export{g as default}; diff --git a/backend/src/main/resources/static/assets/Company-qD6qaVvP.css b/backend/src/main/resources/static/assets/Company-qD6qaVvP.css new file mode 100644 index 0000000..77a1d20 --- /dev/null +++ b/backend/src/main/resources/static/assets/Company-qD6qaVvP.css @@ -0,0 +1 @@ +.sub-nav{background:var(--white);border-bottom:1px solid var(--gray-200)}.sub-nav .container{display:flex;gap:0;overflow-x:auto}.sub-nav-item{padding:14px 22px;font-size:14px;font-weight:500;color:var(--gray-600);white-space:nowrap;border-bottom:2px solid transparent;transition:all var(--fast) var(--ease)}.sub-nav-item:hover{color:var(--primary)}.sub-nav-item.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:700}.ceo-wrap{display:grid;grid-template-columns:220px 1fr;gap:60px;align-items:start}.ceo-photo{text-align:center}.ceo-avatar{width:160px;height:160px;border-radius:50%;background:linear-gradient(135deg,var(--primary),var(--accent));display:flex;align-items:center;justify-content:center;font-size:28px;font-weight:900;color:#fff;margin:0 auto 16px}.ceo-name{font-size:18px;font-weight:700;color:var(--gray-800)}.ceo-sign{font-size:13px;color:var(--gray-500);margin-top:4px}.ceo-text h2{font-size:26px;font-weight:900;color:var(--gray-900);line-height:1.4}.ceo-para{font-size:15px;line-height:1.9;color:var(--gray-700);margin-bottom:18px}.timeline{position:relative;padding-left:0}.timeline-row{display:grid;grid-template-columns:120px 24px 1fr;gap:0 24px;margin-bottom:40px;align-items:start}.timeline-year{font-size:22px;font-weight:900;color:var(--primary);text-align:right;padding-top:2px;line-height:1.2}.timeline-dot{width:16px;height:16px;border-radius:50%;background:var(--primary);border:3px solid var(--primary-light);margin-top:4px;position:relative;flex-shrink:0}.timeline-dot:after{content:"";position:absolute;top:100%;left:50%;transform:translate(-50%);width:2px;height:calc(100% + 40px);background:var(--gray-200)}.timeline-row:last-child .timeline-dot:after{display:none}.timeline-content{padding-bottom:8px}.timeline-item{display:flex;gap:10px;font-size:15px;color:var(--gray-700);margin-bottom:8px;line-height:1.6;align-items:flex-start}.timeline-bullet{width:6px;height:6px;border-radius:50%;background:var(--accent);flex-shrink:0;margin-top:7px}.org-chart{text-align:center}.org-top{display:flex;justify-content:center;margin-bottom:0}.org-box{padding:14px 28px;border-radius:10px;font-weight:700;font-size:15px;display:inline-flex;align-items:center;justify-content:center}.org-box.ceo{background:var(--secondary);color:#fff;min-width:160px;font-size:17px}.org-box.dept{background:var(--white);border:2px solid;min-width:140px;font-size:14px}.org-box.team{background:var(--gray-50);border:1px solid var(--gray-200);color:var(--gray-700);font-size:13px;padding:10px 16px;margin-bottom:8px;min-width:120px;font-weight:500}.org-line-v{width:2px;height:40px;background:var(--gray-300);margin:0 auto}.org-line-v-short{width:2px;height:24px;background:var(--gray-300);margin:0 auto}.org-depts{display:flex;justify-content:center;gap:32px;position:relative}.org-dept-col{display:flex;flex-direction:column;align-items:center}.org-teams{display:flex;flex-direction:column;align-items:center;margin-top:12px}.org-line-h{width:100%;height:2px;background:var(--gray-300)}.ci-section{margin-bottom:56px}.ci-title{font-size:20px;font-weight:800;color:var(--gray-900);margin-bottom:24px;padding-bottom:12px;border-bottom:2px solid var(--gray-200)}.ci-logo-wrap{display:flex;gap:24px;flex-wrap:wrap}.ci-logo-box{flex:1;min-width:200px;padding:48px 32px;border-radius:12px;display:flex;flex-direction:column;align-items:center;gap:16px}.ci-logo-box.dark{background:var(--secondary)}.ci-logo-box.light{background:var(--gray-50);border:1px solid var(--gray-200)}.ci-logo-text-wrap{display:flex;flex-direction:column;align-items:center;gap:4px}.ci-logo-mark{font-size:32px;font-weight:900;color:var(--accent);letter-spacing:4px}.ci-logo-sub{font-size:14px;font-weight:600;color:#ffffffb3;letter-spacing:2px}.ci-logo-label{font-size:12px;color:#ffffff80}.ci-colors{display:grid;grid-template-columns:repeat(4,1fr);gap:20px}.ci-color-card{border:1px solid var(--gray-200);border-radius:10px;overflow:hidden}.ci-color-swatch{height:100px}.ci-color-info{padding:16px;display:flex;flex-direction:column;gap:4px}.ci-color-info strong{font-size:14px;font-weight:700}.ci-hex{font-size:12px;font-family:monospace;color:var(--primary)}.ci-cmyk{font-size:11px;color:var(--gray-500)}.ci-usage{font-size:12px;color:var(--gray-600)}.ci-slogan-wrap{background:linear-gradient(135deg,var(--secondary),var(--primary-dark));border-radius:16px;padding:56px;text-align:center}.ci-slogan-main{font-size:28px;font-weight:900;color:#fff;margin-bottom:12px}.ci-slogan-sub{font-size:16px;color:#ffffffb3}.map-wrap{margin-bottom:32px}.map-placeholder{height:320px;background:var(--gray-100);border-radius:16px;display:flex;flex-direction:column;align-items:center;justify-content:center;border:1px solid var(--gray-200);color:var(--gray-600);font-size:16px;font-weight:600}.map-pin{font-size:48px;margin-bottom:12px}.location-grid{display:grid;grid-template-columns:1fr 1fr;gap:24px}.loc-office-title{font-size:18px;font-weight:700;margin-bottom:20px;color:var(--gray-900)}.loc-table{width:100%;border-collapse:collapse}.loc-table th{width:90px;text-align:left;font-size:13px;color:var(--gray-500);font-weight:600;padding:10px 0;vertical-align:top}.loc-table td{font-size:14px;color:var(--gray-700);padding:10px 0;line-height:1.6;border-bottom:1px solid var(--gray-100)}.transport-list{display:flex;flex-direction:column;gap:20px}.transport-header{display:flex;gap:8px;align-items:center;margin-bottom:6px}.transport-header strong{font-size:14px;font-weight:700;color:var(--primary)}.transport-item p{font-size:13px;color:var(--gray-600);padding-left:24px;line-height:1.7}@media (max-width: 768px){.ceo-wrap{grid-template-columns:1fr}.timeline-row{grid-template-columns:80px 20px 1fr;gap:0 16px}.timeline-year{font-size:16px}.org-depts{flex-direction:column;align-items:center;gap:24px}.ci-colors{grid-template-columns:repeat(2,1fr)}.location-grid{grid-template-columns:1fr}} diff --git a/backend/src/main/resources/static/assets/Contact-C2ZwoM3_.css b/backend/src/main/resources/static/assets/Contact-C2ZwoM3_.css new file mode 100644 index 0000000..efc5836 --- /dev/null +++ b/backend/src/main/resources/static/assets/Contact-C2ZwoM3_.css @@ -0,0 +1 @@ +.contact-page{padding-top:var(--header-h)}.page-hero{background:linear-gradient(135deg,var(--secondary),var(--primary-dark));padding:60px 0;color:#fff}.page-hero-title{font-size:40px;font-weight:900;margin:8px 0 12px}.page-hero p{color:#ffffffbf;font-size:16px}.contact-grid{display:grid;grid-template-columns:340px 1fr;gap:48px;align-items:start}.contact-info h2{font-size:22px;font-weight:700;margin-bottom:28px}.info-item{display:flex;gap:16px;margin-bottom:24px;align-items:flex-start}.info-icon{font-size:24px}.info-item strong{display:block;font-size:13px;font-weight:700;color:var(--gray-500);margin-bottom:2px}.info-item p{font-size:15px;color:var(--gray-800)}.contact-form{padding:36px}.contact-form h2{font-size:22px;font-weight:700;margin-bottom:24px}.form-alert{padding:12px 16px;border-radius:var(--radius-sm);font-size:14px;margin-bottom:16px}.form-alert.success{background:#d1fae5;color:#065f46}.form-alert.error{background:#fee2e2;color:#991b1b}.form-row{display:grid;grid-template-columns:1fr 1fr;gap:16px}.form-group{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}.form-group label{font-size:13px;font-weight:600;color:var(--gray-700)}.required{color:var(--danger)}.form-group input,.form-group select,.form-group textarea{padding:10px 14px;border:1px solid var(--gray-200);border-radius:var(--radius-sm);font-size:14px;font-family:inherit;transition:border-color var(--fast);outline:none}.form-group input:focus,.form-group select:focus,.form-group textarea:focus{border-color:var(--primary);box-shadow:0 0 0 3px #0051a21a}.privacy-agree{display:flex;align-items:center;gap:10px;font-size:13px;color:var(--gray-600);margin-bottom:20px;cursor:pointer}.privacy-agree a{color:var(--primary)}@media (max-width: 1024px){.contact-grid{grid-template-columns:1fr}}@media (max-width: 768px){.form-row{grid-template-columns:1fr}} diff --git a/backend/src/main/resources/static/assets/Contact-C6p_tBWi.js b/backend/src/main/resources/static/assets/Contact-C6p_tBWi.js new file mode 100644 index 0000000..f6d6140 --- /dev/null +++ b/backend/src/main/resources/static/assets/Contact-C6p_tBWi.js @@ -0,0 +1 @@ +import{r as i,j as e}from"./index-ChpGil2q.js";import{a as j}from"./index-DcNlVx-A.js";function y(){const[a,t]=i.useState({name:"",email:"",phone:"",category:"์ œํ’ˆ๋ฌธ์˜",subject:"",content:"",agreePrivacy:!1}),[r,l]=i.useState(null),[o,d]=i.useState(!1),s=n=>{const{name:c,value:m,type:p,checked:x}=n.target;t(u=>({...u,[c]:p==="checkbox"?x:m}))},h=async n=>{if(n.preventDefault(),!a.agreePrivacy){l({type:"error",msg:"๊ฐœ์ธ์ •๋ณด ์ˆ˜์ง‘ยท์ด์šฉ์— ๋™์˜ํ•ด์ฃผ์„ธ์š”."});return}d(!0);try{await j.post("/api/inquiry",a),l({type:"success",msg:"๋ฌธ์˜๊ฐ€ ์ ‘์ˆ˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋น ๋ฅธ ์‹œ์ผ ๋‚ด์— ์—ฐ๋ฝ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค."}),t({name:"",email:"",phone:"",category:"์ œํ’ˆ๋ฌธ์˜",subject:"",content:"",agreePrivacy:!1})}catch{l({type:"error",msg:"๋ฌธ์˜ ์ ‘์ˆ˜ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."})}finally{d(!1)}};return e.jsxs("main",{id:"main-content",className:"contact-page",children:[e.jsx("div",{className:"page-hero",children:e.jsxs("div",{className:"container",children:[e.jsx("span",{className:"section-label",children:"Contact Us"}),e.jsx("h1",{className:"page-hero-title",children:"๋ฌธ์˜ํ•˜๊ธฐ"}),e.jsx("p",{children:"GUARDiA ITSM ๋„์ž… ๋ฌธ์˜ ๋ฐ ์ œํ’ˆ ์ƒ๋‹ด์„ ๋ฐ›์•„๋“œ๋ฆฝ๋‹ˆ๋‹ค."})]})}),e.jsx("section",{className:"section",children:e.jsxs("div",{className:"container contact-grid",children:[e.jsxs("div",{className:"contact-info",children:[e.jsx("h2",{children:"์—ฐ๋ฝ์ฒ˜ ์ •๋ณด"}),[{icon:"๐Ÿ“ž",label:"๋Œ€ํ‘œ์ „ํ™”",value:"02-000-0000"},{icon:"โœ‰๏ธ",label:"์ด๋ฉ”์ผ",value:"info@zioinfo.co.kr"},{icon:"๐Ÿ•",label:"์šด์˜์‹œ๊ฐ„",value:"ํ‰์ผ 09:00 ~ 18:00"},{icon:"๐Ÿ“",label:"์ฃผ์†Œ",value:"์„œ์šธํŠน๋ณ„์‹œ"}].map((n,c)=>e.jsxs("div",{className:"info-item",children:[e.jsx("span",{className:"info-icon",children:n.icon}),e.jsxs("div",{children:[e.jsx("strong",{children:n.label}),e.jsx("p",{children:n.value})]})]},c))]}),e.jsxs("form",{className:"contact-form card",onSubmit:h,children:[e.jsx("h2",{children:"์˜จ๋ผ์ธ ๋ฌธ์˜"}),r&&e.jsxs("div",{className:`form-alert ${r.type}`,children:[r.type==="success"?"โœ…":"โŒ"," ",r.msg]}),e.jsxs("div",{className:"form-row",children:[e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{htmlFor:"name",children:["์„ฑํ•จ ",e.jsx("span",{className:"required",children:"*"})]}),e.jsx("input",{id:"name",name:"name",type:"text",required:!0,value:a.name,onChange:s,placeholder:"ํ™๊ธธ๋™"})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{htmlFor:"phone",children:"์—ฐ๋ฝ์ฒ˜"}),e.jsx("input",{id:"phone",name:"phone",type:"tel",value:a.phone,onChange:s,placeholder:"010-0000-0000"})]})]}),e.jsxs("div",{className:"form-row",children:[e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{htmlFor:"email",children:["์ด๋ฉ”์ผ ",e.jsx("span",{className:"required",children:"*"})]}),e.jsx("input",{id:"email",name:"email",type:"email",required:!0,value:a.email,onChange:s,placeholder:"your@email.com"})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{htmlFor:"category",children:"๋ฌธ์˜ ์œ ํ˜•"}),e.jsxs("select",{id:"category",name:"category",value:a.category,onChange:s,children:[e.jsx("option",{children:"์ œํ’ˆ๋ฌธ์˜"}),e.jsx("option",{children:"๋ฐ๋ชจ ์‹ ์ฒญ"}),e.jsx("option",{children:"๊ธฐ์ˆ ์ง€์›"}),e.jsx("option",{children:"์‚ฌ์—…์ œ์•ˆ"}),e.jsx("option",{children:"์ฑ„์šฉ๋ฌธ์˜"}),e.jsx("option",{children:"๊ธฐํƒ€"})]})]})]}),e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{htmlFor:"subject",children:["์ œ๋ชฉ ",e.jsx("span",{className:"required",children:"*"})]}),e.jsx("input",{id:"subject",name:"subject",type:"text",required:!0,value:a.subject,onChange:s,placeholder:"๋ฌธ์˜ ์ œ๋ชฉ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”"})]}),e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{htmlFor:"content",children:["๋ฌธ์˜ ๋‚ด์šฉ ",e.jsx("span",{className:"required",children:"*"})]}),e.jsx("textarea",{id:"content",name:"content",rows:6,required:!0,value:a.content,onChange:s,placeholder:"๋ฌธ์˜ ๋‚ด์šฉ์„ ์ž์„ธํžˆ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”."})]}),e.jsxs("label",{className:"privacy-agree",children:[e.jsx("input",{type:"checkbox",name:"agreePrivacy",checked:a.agreePrivacy,onChange:s}),e.jsxs("span",{children:["๊ฐœ์ธ์ •๋ณด ์ˆ˜์ง‘ยท์ด์šฉ์— ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ",e.jsx("a",{href:"/privacy",target:"_blank",children:"[๋ณด๊ธฐ]"})]})]}),e.jsx("button",{type:"submit",className:"btn btn-primary btn-lg",style:{width:"100%"},disabled:o,children:o?"์ „์†ก ์ค‘...":"๋ฌธ์˜ ์ ‘์ˆ˜ํ•˜๊ธฐ"})]})]})})]})}export{y as default}; diff --git a/backend/src/main/resources/static/assets/GuardiaDetail-5Pm8bk4O.js b/backend/src/main/resources/static/assets/GuardiaDetail-5Pm8bk4O.js new file mode 100644 index 0000000..ce2332d --- /dev/null +++ b/backend/src/main/resources/static/assets/GuardiaDetail-5Pm8bk4O.js @@ -0,0 +1 @@ +import{r as n,j as s,L as d}from"./index-ChpGil2q.js";const r=[{icon:"๐Ÿค–",title:"AI ์—์ด์ „ํŠธ ์ž๋™ํ™”",desc:"Ollama ์˜จํ”„๋ ˆ๋ฏธ์Šค sLLM ๊ธฐ๋ฐ˜. ๋ฉ”์‹ ์ € ํ•œ ์ค„ ๋ช…๋ น โ†’ ์ž์—ฐ์–ด ํŒŒ์‹ฑ โ†’ ์ž๋™ ๋ฐฐํฌยท์šด์˜. ์™ธ๋ถ€ API ์™„์ „ ์ฐจ๋‹จ์œผ๋กœ ํ์‡„๋ง ํ™˜๊ฒฝ ์ตœ์ ํ™”."},{icon:"๐Ÿ”ง",title:"์—์ด์ „ํŠธ๋ฆฌ์Šค ์•„ํ‚คํ…์ฒ˜",desc:"๋Œ€์ƒ ์„œ๋ฒ„์— ์–ด๋–ค ์†Œํ”„ํŠธ์›จ์–ด๋„ ์„ค์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ‘œ์ค€ SSH/SFTP ํ”„๋กœํ† ์ฝœ๋งŒ์œผ๋กœ ๋ ˆ๊ฑฐ์‹œ WAS(Tomcat/JBoss/WebLogic)๋ฅผ ์›๊ฒฉ ๊ด€๋ฆฌ."},{icon:"๐Ÿ’ฌ",title:"ChatOps ๋ฉ”์‹ ์ € ํ†ตํ•ฉ",desc:"์นด์นด์˜ค์›Œํฌ, ๋„ค์ด๋ฒ„์›์Šค, ์Šฌ๋ž™ ๋“ฑ ์ต์ˆ™ํ•œ ๋ฉ”์‹ ์ €์—์„œ /deploy, /status, /incident ๋ช…๋ น์œผ๋กœ ์ธํ”„๋ผ๋ฅผ ์ฆ‰์‹œ ์ œ์–ด."},{icon:"๐Ÿ“Š",title:"ํ†ตํ•ฉ ITSM ๋Œ€์‹œ๋ณด๋“œ",desc:"SRยท์ธ์‹œ๋˜ํŠธยท๋ณ€๊ฒฝ๊ด€๋ฆฌยทSLAยทCMDBยท์˜ˆ์ธก ์œ ์ง€๋ณด์ˆ˜๋ฅผ ๋‹จ์ผ ํ”Œ๋žซํผ์—์„œ ๊ด€๋ฆฌ. 7์ผ ์ถ”์ด ์ฐจํŠธ์™€ AI ์ธ์‚ฌ์ดํŠธ ์‹ค์‹œ๊ฐ„ ์ œ๊ณต."},{icon:"๐Ÿ”’",title:"์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ๋ณด์•ˆ",desc:"AES-256-GCM ์•”ํ˜ธํ™”, MFA/OTP, PAM ํŠน๊ถŒ์ ‘๊ทผ๊ด€๋ฆฌ, SHA-256 ํ•ด์‹œ์ฒด์ธ ๋ถˆ๋ณ€ ๊ฐ์‚ฌ๋กœ๊ทธ, Zero Trust ์ง€์† ์ธ์ฆ."},{icon:"๐Ÿ—๏ธ",title:"PMS ํ”„๋กœ์ ํŠธ ๊ด€๋ฆฌ",desc:"WBSยท์‚ฐ์ถœ๋ฌผยท์ผ๊ฐ„/์ฃผ๊ฐ„/์›”๊ฐ„ ์ž๋™ ๋ณด๊ณ ์„œ(ExcelยทPDFยทPPT). ์ด์Šˆยท์œ„ํ—˜ ๊ด€๋ฆฌ, Gitea ์—ฐ๋™, Jenkins CI/CD ํŒŒ์ดํ”„๋ผ์ธ."},{icon:"๐ŸŒ",title:"๊ณต๊ณต๊ธฐ๊ด€ ํ•„์ˆ˜ ์ค€์ˆ˜",desc:"ํ–‰์•ˆ๋ถ€ SW ๋ณด์•ˆ์•ฝ์  ์ž๋™ ์ ๊ฒ€, KWCAG 2.1 ์›น์ ‘๊ทผ์„ฑ, ๊ฐœ์ธ์ •๋ณด๋ณดํ˜ธ๋ฒ• ์ค€์ˆ˜ ์Šค์บ”. 19๊ฐœ ๊ณต๊ณต๊ธฐ๊ด€ ์ฒดํฌ๋ฆฌ์ŠคํŠธ ๋‚ด์žฅ."},{icon:"๐Ÿ“ก",title:"Scouter APM ๋ชจ๋‹ˆํ„ฐ๋ง",desc:"Java WAS(Tomcat/JBoss) ์ „๋ฌธ APM. CPUยทHeapยทTPSยท์‘๋‹ต์‹œ๊ฐ„ ์‹ค์‹œ๊ฐ„ ์ˆ˜์ง‘, ์ด์ƒ ํƒ์ง€ ์‹œ ์ž๋™ ์ธ์‹œ๋˜ํŠธ ์ƒ์„ฑ."}],o=[{name:"COMMUNITY",badge:"๋ฌด๋ฃŒ",color:"#10B981",target:"์†Œ๊ทœ๋ชจ ๊ธฐ๊ด€ยท๊ฒ€ํ† ์šฉ",features:["๊ธฐ๋ณธ SR ๊ด€๋ฆฌ (๋ฌด์ œํ•œ)","CMDB ์„œ๋ฒ„ 20๋Œ€","์‚ฌ์šฉ์ž 10๋ช…","๋Œ€์‹œ๋ณด๋“œ","๋ด‡ ๊ธฐ๋ณธ ๋ช…๋ น์–ด"],cta:"๋ฌด๋ฃŒ ์‹œ์ž‘",href:"/support/contact?type=community"},{name:"STANDARD",badge:"๊ถŒ์žฅ",color:"var(--primary)",target:"์ค‘ํ˜• ๊ธฐ๊ด€",features:["์ „์ฒด ITSM ๊ธฐ๋Šฅ","AI ์—์ด์ „ํŠธ ์ž๋™ํ™”","LDAP/AD ์—ฐ๋™","MFA ๋ณด์•ˆ","SLA ๊ด€๋ฆฌ","PMS ๊ธฐ๋ณธ"],cta:"๋„์ž… ๋ฌธ์˜",href:"/support/contact?type=standard",highlight:!0},{name:"ENTERPRISE",badge:"๋งž์ถค",color:"#6366F1",target:"๋Œ€ํ˜• ๊ด€๊ณต์„œยท๊ด‘์—ญ๊ธฐ๊ด€",features:["๋ฌด์ œํ•œ ์„œ๋ฒ„ยท๊ธฐ๊ด€","์ทจ์•ฝ์  ์ž๋™ ์Šค์บ”","Scouter APM","FinOps ๋น„์šฉ ๋ถ„์„","SIEM ์—ฐ๋™","์ „๋‹ด ๊ธฐ์ˆ  ์ง€์›"],cta:"์ „๋ฌธ๊ฐ€ ์ƒ๋‹ด",href:"/support/contact?type=enterprise"}],m=[{cmd:"/sr <์ œ๋ชฉ>",desc:"SR(์„œ๋น„์Šค์š”์ฒญ) ์ฆ‰์‹œ ์ ‘์ˆ˜",cat:"SR ๊ด€๋ฆฌ"},{cmd:"/status",desc:"์‹œ์Šคํ…œ ์ „์ฒด ํ˜„ํ™ฉ ์š”์•ฝ",cat:"SR ๊ด€๋ฆฌ"},{cmd:"/assign <๋‹ด๋‹น์ž>",desc:"SR ๋‹ด๋‹น์ž ์ฆ‰์‹œ ๋ฐฐ์ •",cat:"SR ๊ด€๋ฆฌ"},{cmd:"/approve ",desc:"SR ์ฆ‰์‹œ ์Šน์ธ",cat:"SR ๊ด€๋ฆฌ"},{cmd:"/sla",desc:"SLA ์œ„๋ฐ˜ ํ˜„ํ™ฉ ๋ชฉ๋ก",cat:"SR ๊ด€๋ฆฌ"},{cmd:"/incident <์ œ๋ชฉ> [P1~P4]",desc:"์ธ์‹œ๋˜ํŠธ ๋น ๋ฅธ ๋“ฑ๋ก",cat:"์ธ์‹œ๋˜ํŠธ"},{cmd:"/oncall",desc:"ํ˜„์žฌ ๋‹น์ง์ž ์ฆ‰์‹œ ์กฐํšŒ",cat:"์ธ์‹œ๋˜ํŠธ"},{cmd:"/rca ",desc:"AI ์ž๋™ RCA ๊ทผ๋ณธ์›์ธ ๋ถ„์„",cat:"์ธ์‹œ๋˜ํŠธ"},{cmd:"/escalate ",desc:"๋‹น์ง์ž์—๊ฒŒ ์—์Šค์ปฌ๋ ˆ์ด์…˜",cat:"์ธ์‹œ๋˜ํŠธ"},{cmd:"!deploy <์„ธ์…˜ID>",desc:"WAS ๋ฐฐํฌ ์‹คํ–‰ (SSH)",cat:"๋ฐฐํฌ ์ œ์–ด"},{cmd:"/rollback <์„ธ์…˜ID>",desc:"๊ธด๊ธ‰ ๋กค๋ฐฑ",cat:"๋ฐฐํฌ ์ œ์–ด"},{cmd:"!health <์„œ๋ฒ„๋ช…>",desc:"์„œ๋ฒ„ ํ—ฌ์Šค์ฒดํฌ",cat:"๋ฐฐํฌ ์ œ์–ด"},{cmd:"/pms <ํ”„๋กœ์ ํŠธ์ฝ”๋“œ>",desc:"ํ”„๋กœ์ ํŠธ ์ง„์ฒ™ ํ˜„ํ™ฉ",cat:"PMS"},{cmd:"/report <์ฝ”๋“œ> weekly",desc:"์ฃผ๊ฐ„ ๋ณด๊ณ ์„œ ๋ฉ”์‹ ์ € ๋ฐœ์†ก",cat:"PMS"},{cmd:"/wbs <์ฝ”๋“œ>",desc:"WBS ์ง€์—ฐ ํ˜„ํ™ฉ",cat:"PMS"},{cmd:"/scouter <์„œ๋ฒ„๋ช…>",desc:"Scouter APM ์‹ค์‹œ๊ฐ„ ๋ฉ”ํŠธ๋ฆญ",cat:"๋ชจ๋‹ˆํ„ฐ๋ง"},{cmd:"/scan",desc:"์‹œํ์–ด์ฝ”๋”ฉยท๋ณด์•ˆ ์ž๋™ ์ ๊ฒ€",cat:"๋ณด์•ˆ"},{cmd:"/vuln <์„œ๋ฒ„|IP>",desc:"์ทจ์•ฝ์  ์Šค์บ”",cat:"๋ณด์•ˆ"},{cmd:"/notify <๋ฉ”์‹œ์ง€>",desc:"์šด์˜ํŒ€ ์ „์ฒด ๊ณต์ง€ ๋ฐœ์†ก",cat:"๊ณต์ง€"}],h=[{name:"์นด์นด์˜ค์›Œํฌ",icon:"๐Ÿ’ฌ",color:"#FAE100",textColor:"#3C1E1E",desc:"๊ฒฐ์žฌ ๋ฒ„ํŠผ + ๋ด‡ ๋ช…๋ น ์™„๋ฒฝ ์ง€์›"},{name:"๋„ค์ด๋ฒ„์›์Šค",icon:"๐ŸŸข",color:"#03C75A",textColor:"#fff",desc:"Flex ๋ฉ”์‹œ์ง€ + Rich ๊ฒฐ๊ณผ ํ‘œ์‹œ"},{name:"์Šฌ๋ž™",icon:"๐Ÿ’œ",color:"#611F69",textColor:"#fff",desc:"์Šฌ๋ž˜์‹œ ๋ช…๋ น + ๋ธ”๋กํ‚ท UI"},{name:"์ž์ฒด ๋ฉ”์‹ ์ €",icon:"๐Ÿ”ต",color:"#0051A2",textColor:"#fff",desc:"GUARDiA ๋‚ด์žฅ Slackํ˜• ๋ฉ”์‹ ์ €"}],x=[{category:"Backend",items:["Python 3.11 / FastAPI","SQLAlchemy Async","PostgreSQL / SQLite"]},{category:"AIยทLLM",items:["Ollama (์˜จํ”„๋ ˆ๋ฏธ์Šค)","llama3.1:8b / codellama","์™ธ๋ถ€ API ์™„์ „ ์ฐจ๋‹จ"]},{category:"Infra",items:["paramiko SSH/SFTP","์—์ด์ „ํŠธ๋ฆฌ์Šค","AES-256-GCM ์•”ํ˜ธํ™”"]},{category:"Frontend",items:["React.js / PWA","Chart.js ๋Œ€์‹œ๋ณด๋“œ","D3.js ํ† ํด๋กœ์ง€"]},{category:"DevOps",items:["Jenkins CI/CD","Gitea ํ˜•์ƒ๊ด€๋ฆฌ","Docker / K8s"]},{category:"๋ชจ๋‹ˆํ„ฐ๋ง",items:["Scouter APM","Prometheus/Grafana","ELK/Splunk SIEM"]}];function N(){const[i,t]=n.useState("features");return s.jsxs("main",{id:"main-content",className:"guardia-page",children:[s.jsxs("section",{className:"gd-hero",children:[s.jsx("div",{className:"gd-hero-overlay"}),s.jsxs("div",{className:"container gd-hero-inner",children:[s.jsxs("div",{className:"gd-hero-text",children:[s.jsx("span",{className:"badge badge-new",style:{fontSize:"12px",padding:"4px 12px"},children:"NEW v2.0"}),s.jsxs("h1",{className:"gd-hero-title",children:[s.jsx("span",{children:"GUARDiA"})," ITSM"]}),s.jsxs("p",{className:"gd-hero-sub",children:["AI ๊ธฐ๋ฐ˜ ๋ ˆ๊ฑฐ์‹œ ์ธํ”„๋ผ ์ž์œจ ์šด์˜ ํ”Œ๋žซํผ",s.jsx("br",{}),s.jsx("strong",{children:"๋ฉ”์‹ ์ € ํ•œ ์ค„ ๋ช…๋ น"}),"์œผ๋กœ 1,000๊ฐœ ๊ด€๊ณต์„œ ์ธํ”„๋ผ๋ฅผ ์ž๋™ํ™”"]}),s.jsxs("div",{className:"gd-hero-actions",children:[s.jsx(d,{to:"/support/contact?type=demo",className:"btn btn-white btn-lg",children:"๋ฌด๋ฃŒ ๋ฐ๋ชจ ์‹ ์ฒญ"}),s.jsx("a",{href:"#features",className:"btn btn-lg",style:{color:"rgba(255,255,255,.85)",border:"1px solid rgba(255,255,255,.3)"},children:"๊ธฐ๋Šฅ ์‚ดํŽด๋ณด๊ธฐ โ†“"})]})]}),s.jsx("div",{className:"gd-hero-stats",children:[{val:"1,000+",lab:"๊ด€๋ฆฌ ๊ธฐ๊ด€"},{val:"99.9%",lab:"๊ฐ€์šฉ์„ฑ"},{val:"70%",lab:"์šด์˜ ๋น„์šฉ ์ ˆ๊ฐ"},{val:"0๊ฐœ",lab:"๋Œ€์ƒ ์„œ๋ฒ„ ์ถ”๊ฐ€ ์„ค์น˜"}].map((e,c)=>s.jsxs("div",{className:"gd-stat",children:[s.jsx("div",{className:"gd-stat-val",children:e.val}),s.jsx("div",{className:"gd-stat-lab",children:e.lab})]},c))})]})]}),s.jsx("div",{className:"gd-tabs-bar",children:s.jsx("div",{className:"container",children:s.jsx("div",{className:"gd-tabs",children:[{id:"features",label:"ํ•ต์‹ฌ ๊ธฐ๋Šฅ"},{id:"messenger",label:"Messenger Bot"},{id:"editions",label:"์—๋””์…˜ ๋น„๊ต"},{id:"tech",label:"๊ธฐ์ˆ  ์Šคํƒ"},{id:"usecase",label:"๋„์ž… ์‚ฌ๋ก€"}].map(e=>s.jsx("button",{className:`gd-tab ${i===e.id?"active":""}`,onClick:()=>t(e.id),children:e.label},e.id))})})}),i==="features"&&s.jsx("section",{id:"features",className:"section",children:s.jsxs("div",{className:"container",children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Core Features"}),s.jsxs("h2",{className:"section-title",children:["GUARDiA๊ฐ€ ์ œ๊ณตํ•˜๋Š”",s.jsx("br",{}),s.jsx("em",{children:"8๊ฐ€์ง€ ํ•ต์‹ฌ ๊ธฐ๋Šฅ"})]}),s.jsx("div",{className:"divider"})]}),s.jsx("div",{className:"gd-screenshots",children:[{file:"01_dashboard",caption:"ํ†ตํ•ฉ ๋Œ€์‹œ๋ณด๋“œ โ€” SRยทSLAยทAI ์ธ์‚ฌ์ดํŠธ"},{file:"02_sr_list",caption:"SR ์„œ๋น„์Šค ์š”์ฒญ โ€” ์นธ๋ฐ˜/๋ชฉ๋ก ๋ทฐ"},{file:"03_si_project",caption:"PMS ํ”„๋กœ์ ํŠธ โ€” WBSยท์‚ฐ์ถœ๋ฌผยท๋ณด๊ณ ์„œ"},{file:"04_incidents",caption:"์ธ์‹œ๋˜ํŠธ ๊ด€๋ฆฌ โ€” AI ์ž๋™ RCA"},{file:"05_agents",caption:"AI ์—์ด์ „ํŠธ โ€” Ollama ์˜จํ”„๋ ˆ๋ฏธ์Šค"},{file:"06_license",caption:"๋ผ์ด์„ ์Šค ๊ด€๋ฆฌ โ€” ์—๋””์…˜ยท์ฒดํ—˜ํŒ"}].map((e,c)=>s.jsxs("div",{className:"screenshot-card",children:[s.jsx("img",{src:`/screenshots/${e.file}.png`,alt:e.caption,className:"screenshot-img",onError:a=>{a.target.style.display="none",a.target.nextSibling.style.display="flex"}}),s.jsxs("div",{className:"screenshot-placeholder",style:{display:"none"},children:[s.jsx("span",{className:"icon",children:"๐Ÿ–ฅ๏ธ"}),s.jsx("span",{children:"์ค€๋น„ ์ค‘"})]}),s.jsx("div",{className:"screenshot-caption",children:e.caption})]},c))}),s.jsx("div",{className:"section-header",style:{marginTop:"60px",marginBottom:"32px"},children:s.jsx("h3",{style:{fontSize:"28px",fontWeight:"800",color:"var(--gray-900)"},children:"ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์ƒ์„ธ"})}),s.jsx("div",{className:"gd-features-grid",children:r.map((e,c)=>s.jsxs("div",{className:"gd-feature-card card",children:[s.jsx("div",{className:"gd-feature-icon",children:e.icon}),s.jsx("h3",{children:e.title}),s.jsx("p",{children:e.desc})]},c))})]})}),i==="messenger"&&s.jsx("section",{className:"section",children:s.jsxs("div",{className:"container",children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"ChatOps Messenger"}),s.jsxs("h2",{className:"section-title",children:["๋ฉ”์‹ ์ € ํ•˜๋‚˜๋กœ",s.jsx("br",{}),s.jsx("em",{children:"๋ชจ๋“  ์ธํ”„๋ผ๋ฅผ ์ œ์–ด"})]}),s.jsx("div",{className:"divider"}),s.jsxs("p",{className:"section-desc",children:["์ต์ˆ™ํ•œ ๋ฉ”์‹ ์ €์—์„œ ๋ช…๋ น์–ด ํ•˜๋‚˜๋กœ ์„œ๋ฒ„ ๋ฐฐํฌยท์žฅ์•  ๋Œ€์‘ยท๋ณด๊ณ ์„œ ๋ฐœ์†ก๊นŒ์ง€.",s.jsx("br",{}),"GUARDiA Bot์€ 25๊ฐœ ๋ช…๋ น์–ด๋กœ IT ์šด์˜์˜ ๋ชจ๋“  ์ˆœ๊ฐ„์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค."]})]}),s.jsx("div",{className:"messenger-platforms",children:h.map((e,c)=>s.jsxs("div",{className:"messenger-platform",style:{background:e.color},children:[s.jsx("span",{className:"platform-icon",children:e.icon}),s.jsxs("div",{children:[s.jsx("strong",{style:{color:e.textColor},children:e.name}),s.jsx("p",{style:{color:e.textColor,opacity:.85},children:e.desc})]})]},c))}),s.jsxs("div",{className:"cmd-catalog",children:[s.jsx("h3",{className:"cmd-catalog-title",children:"25๊ฐœ ๋ด‡ ๋ช…๋ น์–ด ์ „์ฒด ๋ชฉ๋ก"}),["SR ๊ด€๋ฆฌ","์ธ์‹œ๋˜ํŠธ","๋ฐฐํฌ ์ œ์–ด","PMS","๋ชจ๋‹ˆํ„ฐ๋ง","๋ณด์•ˆ","๊ณต์ง€"].map(e=>{const c=m.filter(a=>a.cat===e);return s.jsxs("div",{className:"cmd-group",children:[s.jsx("h4",{className:"cmd-group-title",children:e}),s.jsx("div",{className:"cmd-list",children:c.map((a,l)=>s.jsxs("div",{className:"cmd-item",children:[s.jsx("code",{className:"cmd-code",children:a.cmd}),s.jsx("span",{className:"cmd-desc",children:a.desc})]},l))})]},e)})]}),s.jsxs("div",{className:"messenger-demo",children:[s.jsx("h3",{className:"demo-title",children:"์‹ค์ œ ์šด์˜ ์‹œ๋‚˜๋ฆฌ์˜ค"}),s.jsxs("div",{className:"demo-scenario",children:[s.jsxs("div",{className:"demo-step",children:[s.jsx("div",{className:"step-num",children:"1"}),s.jsxs("div",{className:"step-content",children:[s.jsx("strong",{children:"์žฅ์•  ํƒ์ง€"}),s.jsx("p",{children:"Scouter APM์ด ์„œ๋ฒ„ CPU 90% ๊ฐ์ง€ โ†’ ์ž๋™์œผ๋กœ GUARDiA ์šด์˜ ์ฑ„๋„์— ๊ฒฝ๋ณด ๋ฐœ์†ก"}),s.jsx("div",{className:"chat-bubble bot",children:"๐Ÿšจ web-01 CPU 90.3% โ€” P2 ์ธ์‹œ๋˜ํŠธ ์ž๋™ ๋“ฑ๋ก: INC-20260530-A1B2C3"})]})]}),s.jsxs("div",{className:"demo-step",children:[s.jsx("div",{className:"step-num",children:"2"}),s.jsxs("div",{className:"step-content",children:[s.jsx("strong",{children:"๋‹ด๋‹น์ž ์ฆ‰์‹œ ๋Œ€์‘"}),s.jsx("p",{children:"๋ฉ”์‹ ์ €์—์„œ RCA ๋ถ„์„ ์š”์ฒญ"}),s.jsx("div",{className:"chat-bubble user",children:"/rca INC-20260530-A1B2C3"}),s.jsxs("div",{className:"chat-bubble bot",children:["๐Ÿค– AI RCA ๋ถ„์„ ์™„๋ฃŒ",s.jsx("br",{}),"๊ทผ๋ณธ์›์ธ: ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ (Heap 98%)",s.jsx("br",{}),"์žฌ๋ฐœ๋ฐฉ์ง€: WAS ์žฌ๊ธฐ๋™ + ํž™๋คํ”„ ๋ถ„์„",s.jsx("br",{}),"์‹ ๋ขฐ๋„: 87%"]})]})]}),s.jsxs("div",{className:"demo-step",children:[s.jsx("div",{className:"step-num",children:"3"}),s.jsxs("div",{className:"step-content",children:[s.jsx("strong",{children:"์›๊ฒฉ ์กฐ์น˜ ์‹คํ–‰"}),s.jsx("p",{children:"SSH ์žฌ๊ธฐ๋™ ๋ช…๋ น ์‹คํ–‰"}),s.jsx("div",{className:"chat-bubble user",children:"!sm web-01 tomcat_restart"}),s.jsxs("div",{className:"chat-bubble bot",children:["โœ… web-01 Tomcat ์žฌ๊ธฐ๋™ ์™„๋ฃŒ",s.jsx("br",{}),"์†Œ์š”: 38์ดˆ | CPU: 12% | ์ •์ƒํ™”"]})]})]}),s.jsxs("div",{className:"demo-step",children:[s.jsx("div",{className:"step-num",children:"4"}),s.jsxs("div",{className:"step-content",children:[s.jsx("strong",{children:"์ž๋™ ๋ณด๊ณ "}),s.jsx("p",{children:"์ธ์‹œ๋˜ํŠธ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ ์ž๋™ ๋ณด๊ณ ์„œ ๋ฐœ์†ก"}),s.jsx("div",{className:"chat-bubble user",children:"/notify 22:15 web-01 ์„œ๋ฒ„ ์žฅ์•  ๋ณต๊ตฌ ์™„๋ฃŒ. ์›์ธ: ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์žฌ๋ฐœ ๋ฐฉ์ง€ ์กฐ์น˜ ์™„๋ฃŒ."}),s.jsx("div",{className:"chat-bubble bot",children:"โœ… ์šด์˜ํŒ€ ์ „์ฒด ๊ณต์ง€ ๋ฐœ์†ก ์™„๋ฃŒ (ops ์ฑ„๋„)"})]})]})]})]})]})}),i==="editions"&&s.jsx("section",{className:"section",children:s.jsxs("div",{className:"container",children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Editions"}),s.jsxs("h2",{className:"section-title",children:["๊ธฐ๊ด€ ๊ทœ๋ชจ์— ๋งž๋Š”",s.jsx("br",{}),s.jsx("em",{children:"์—๋””์…˜ ์„ ํƒ"})]}),s.jsx("div",{className:"divider"})]}),s.jsx("div",{className:"gd-editions-grid",children:o.map((e,c)=>s.jsxs("div",{className:`gd-edition-card ${e.highlight?"highlight":""}`,style:{"--ed-color":e.color},children:[e.highlight&&s.jsx("div",{className:"edition-recommend",children:"์ถ”์ฒœ"}),s.jsxs("div",{className:"edition-header",children:[s.jsx("h3",{children:e.name}),s.jsx("span",{className:"edition-badge",children:e.badge})]}),s.jsx("p",{className:"edition-target",children:e.target}),s.jsx("ul",{className:"edition-features",children:e.features.map((a,l)=>s.jsxs("li",{children:[s.jsx("span",{className:"check",children:"โœ“"}),a]},l))}),s.jsx(d,{to:e.href,className:"btn edition-cta",children:e.cta})]},c))})]})}),i==="tech"&&s.jsx("section",{className:"section",children:s.jsxs("div",{className:"container",children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Technology"}),s.jsxs("h2",{className:"section-title",children:["๊ฒ€์ฆ๋œ",s.jsx("br",{}),s.jsx("em",{children:"๊ธฐ์ˆ  ์Šคํƒ"})]}),s.jsx("div",{className:"divider"}),s.jsx("p",{className:"section-desc",children:"์˜จํ”„๋ ˆ๋ฏธ์Šค ์ „์šฉ ์„ค๊ณ„ โ€” ์™ธ๋ถ€ ํด๋ผ์šฐ๋“œ ์˜์กด ์—†๋Š” ์™„์ „ ํ์‡„๋ง ๋™์ž‘"})]}),s.jsx("div",{className:"gd-tech-grid",children:x.map((e,c)=>s.jsxs("div",{className:"gd-tech-card card",children:[s.jsx("h3",{className:"tech-category",children:e.category}),s.jsx("ul",{className:"tech-items",children:e.items.map((a,l)=>s.jsx("li",{children:a},l))})]},c))})]})}),i==="usecase"&&s.jsx("section",{className:"section",children:s.jsxs("div",{className:"container",children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Use Cases"}),s.jsxs("h2",{className:"section-title",children:["์‹ค์ œ ",s.jsx("em",{children:"๋„์ž… ์‚ฌ๋ก€"})]}),s.jsx("div",{className:"divider"})]}),s.jsx("div",{className:"gd-usecases",children:[{org:"๊ด‘์—ญ ์ง€๋ฐฉ์ž์น˜๋‹จ์ฒด",result:"๋ ˆ๊ฑฐ์‹œ ์„œ๋ฒ„ 200๋Œ€ SSH ์ž๋™ํ™”, ์šด์˜ ์ธ๋ ฅ 3๋ช…โ†’1๋ช…",badge:"์ค‘์•™๋ถ€์ฒ˜"},{org:"๊ณต๊ณต๊ธฐ๊ด€ IT์„ผํ„ฐ",result:"์›”๊ฐ„ SR 500๊ฑด ์ฒ˜๋ฆฌ, AI ์ž๋™๋ถ„๋ฅ˜๋กœ 80% ์‘๋‹ต์‹œ๊ฐ„ ๋‹จ์ถ•",badge:"๊ณต๊ณต๊ธฐ๊ด€"},{org:"์ง€๋ฐฉ ๊ต์œก์ฒญ",result:"Tomcat 100๋Œ€ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์ž๋™ํ™”, ์žฅ์•  ๋Œ€์‘ ์‹œ๊ฐ„ 70% ๋‹จ์ถ•",badge:"๊ต์œก"}].map((e,c)=>s.jsxs("div",{className:"usecase-card card",children:[s.jsx("span",{className:"badge badge-primary",children:e.badge}),s.jsx("h3",{children:e.org}),s.jsx("p",{children:e.result})]},c))})]})}),s.jsx("section",{className:"gd-cta",children:s.jsxs("div",{className:"container",children:[s.jsx("h2",{children:"์ง€๊ธˆ ๋ฐ”๋กœ ๋ฌด๋ฃŒ ๋ฐ๋ชจ๋ฅผ ๊ฒฝํ—˜ํ•ด ๋ณด์„ธ์š”"}),s.jsx("p",{children:"์ „๋ฌธ ์ปจ์„คํ„ดํŠธ๊ฐ€ ๊ท€ ๊ธฐ๊ด€ ํ™˜๊ฒฝ์— ๋งž๋Š” ์ตœ์ ์˜ ๊ตฌ์„ฑ์„ ์ œ์•ˆํ•ด ๋“œ๋ฆฝ๋‹ˆ๋‹ค."}),s.jsxs("div",{className:"gd-cta-actions",children:[s.jsx(d,{to:"/support/contact?type=demo",className:"btn btn-white btn-lg",children:"๋ฌด๋ฃŒ ๋ฐ๋ชจ ์‹ ์ฒญ"}),s.jsx(d,{to:"/support/catalog",className:"btn btn-lg",style:{color:"rgba(255,255,255,.8)",border:"1px solid rgba(255,255,255,.3)"},children:"์ œํ’ˆ ์†Œ๊ฐœ์„œ"})]})]})})]})}export{N as default}; diff --git a/backend/src/main/resources/static/assets/GuardiaDetail-Ax0ubrjA.css b/backend/src/main/resources/static/assets/GuardiaDetail-Ax0ubrjA.css new file mode 100644 index 0000000..9cd92fa --- /dev/null +++ b/backend/src/main/resources/static/assets/GuardiaDetail-Ax0ubrjA.css @@ -0,0 +1 @@ +.guardia-page{padding-top:var(--header-h)}.gd-hero{position:relative;background:linear-gradient(135deg,#0a0f24,#001f5c,#0051a2);padding:80px 0 60px;overflow:hidden}.gd-hero:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:radial-gradient(ellipse at 70% 50%,rgba(0,163,224,.15) 0%,transparent 70%)}.gd-hero-overlay{position:absolute;top:0;right:0;bottom:0;left:0;background:#0003}.gd-hero-inner{position:relative;z-index:2;display:grid;grid-template-columns:1fr auto;gap:60px;align-items:center}.gd-hero-title{font-size:clamp(40px,5vw,64px);font-weight:900;color:#fff;margin:12px 0 16px}.gd-hero-title span{color:var(--accent)}.gd-hero-sub{font-size:18px;color:#fffc;line-height:1.7;margin-bottom:32px}.gd-hero-sub strong{color:#fff}.gd-hero-actions{display:flex;gap:16px;flex-wrap:wrap}.gd-hero-stats{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;flex-shrink:0}.gd-stat{background:#ffffff14;border:1px solid rgba(255,255,255,.15);border-radius:var(--radius);padding:20px 24px;text-align:center}.gd-stat-val{font-size:28px;font-weight:900;color:var(--accent)}.gd-stat-lab{font-size:12px;color:#ffffffb3;margin-top:4px}.gd-tabs-bar{background:var(--white);border-bottom:2px solid var(--gray-200);position:sticky;top:var(--header-h);z-index:100}.gd-tabs{display:flex;gap:0;overflow-x:auto}.gd-tab{padding:16px 24px;font-size:14px;font-weight:600;color:var(--gray-600);border-bottom:3px solid transparent;margin-bottom:-2px;white-space:nowrap;transition:all var(--fast)}.gd-tab:hover{color:var(--primary)}.gd-tab.active{color:var(--primary);border-bottom-color:var(--primary)}.gd-features-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px}.gd-feature-card{padding:28px;display:flex;flex-direction:column;gap:12px}.gd-feature-icon{font-size:36px}.gd-feature-card h3{font-size:16px;font-weight:700;color:var(--gray-900)}.gd-feature-card p{font-size:14px;color:var(--gray-600);line-height:1.7}.gd-screenshots{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-top:40px}.screenshot-card{border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);border:1px solid var(--gray-200);transition:all var(--mid) var(--ease)}.screenshot-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-lg)}.screenshot-img{width:100%;aspect-ratio:16/9;object-fit:cover;display:block}.screenshot-placeholder{width:100%;aspect-ratio:16/9;background:linear-gradient(135deg,#1e2333,#2d3748);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;color:var(--gray-400);font-size:13px}.screenshot-placeholder .icon{font-size:32px}.screenshot-caption{padding:12px 16px;font-size:13px;font-weight:600;color:var(--gray-700);background:var(--white)}.messenger-platforms{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:48px}.messenger-platform{border-radius:var(--radius);padding:20px;display:flex;align-items:center;gap:14px}.platform-icon{font-size:28px;flex-shrink:0}.messenger-platform strong{display:block;font-size:15px;font-weight:700}.messenger-platform p{font-size:12px;margin-top:2px}.cmd-catalog{background:var(--gray-900);border-radius:var(--radius-lg);padding:32px;margin-bottom:48px}.cmd-catalog-title{color:#fff;font-size:18px;font-weight:700;margin-bottom:24px;padding-bottom:12px;border-bottom:1px solid rgba(255,255,255,.1)}.cmd-group{margin-bottom:20px}.cmd-group-title{font-size:11px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:var(--accent);margin-bottom:10px}.cmd-list{display:flex;flex-direction:column;gap:6px}.cmd-item{display:flex;align-items:baseline;gap:16px}.cmd-code{font-family:Courier New,monospace;font-size:13px;color:#a5f3fc;background:#a5f3fc14;padding:3px 8px;border-radius:4px;white-space:nowrap;flex-shrink:0;min-width:220px}.cmd-desc{font-size:13px;color:#ffffffa6}.demo-title{font-size:22px;font-weight:700;color:var(--gray-900);margin-bottom:24px}.demo-scenario{display:flex;flex-direction:column;gap:32px}.demo-step{display:flex;gap:20px}.step-num{width:40px;height:40px;flex-shrink:0;background:var(--primary);color:#fff;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:700}.step-content{flex:1}.step-content strong{font-size:16px;color:var(--gray-900);display:block;margin-bottom:6px}.step-content>p{font-size:14px;color:var(--gray-600);margin-bottom:10px}.chat-bubble{padding:12px 16px;border-radius:12px;font-size:13px;line-height:1.6;margin-top:6px;max-width:520px}.chat-bubble.bot{background:#1e2333;color:#ffffffe6}.chat-bubble.user{background:var(--primary-light);color:var(--primary);font-weight:600}.gd-editions-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}.gd-edition-card{border:2px solid var(--gray-200);border-radius:var(--radius-lg);padding:36px 28px;position:relative;display:flex;flex-direction:column;gap:16px;transition:all var(--mid) var(--ease)}.gd-edition-card:hover{box-shadow:var(--shadow-lg)}.gd-edition-card.highlight{border-color:var(--primary);box-shadow:0 0 0 4px #0051a21a}.edition-recommend{position:absolute;top:-12px;left:50%;transform:translate(-50%);background:var(--primary);color:#fff;font-size:12px;font-weight:700;padding:3px 14px;border-radius:20px}.edition-header{display:flex;align-items:center;justify-content:space-between}.edition-header h3{font-size:22px;font-weight:900;color:var(--ed-color, var(--gray-900))}.edition-badge{background:color-mix(in srgb,var(--ed-color, var(--primary)) 15%,transparent);color:var(--ed-color, var(--primary));padding:3px 10px;border-radius:12px;font-size:12px;font-weight:700}.edition-target{font-size:13px;color:var(--gray-500)}.edition-features{display:flex;flex-direction:column;gap:10px;flex:1}.edition-features li{display:flex;align-items:flex-start;gap:10px;font-size:14px;color:var(--gray-700)}.check{color:var(--ed-color, var(--primary));font-weight:700;flex-shrink:0}.edition-cta{background:var(--ed-color, var(--primary));color:#fff;text-align:center;padding:12px;border-radius:var(--radius);font-weight:600;width:100%;justify-content:center}.gd-tech-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}.gd-tech-card{padding:28px}.tech-category{font-size:16px;font-weight:700;color:var(--primary);margin-bottom:16px}.tech-items{display:flex;flex-direction:column;gap:10px}.tech-items li{font-size:14px;color:var(--gray-700);padding:8px 12px;background:var(--gray-50);border-radius:var(--radius-sm);border-left:3px solid var(--accent)}.gd-usecases{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}.usecase-card{padding:28px;display:flex;flex-direction:column;gap:12px}.usecase-card h3{font-size:17px;font-weight:700;color:var(--gray-900)}.usecase-card p{font-size:14px;color:var(--gray-600);line-height:1.7}.gd-cta{background:linear-gradient(135deg,var(--primary-dark),#0a0f24);padding:80px 0;text-align:center}.gd-cta h2{font-size:32px;font-weight:800;color:#fff;margin-bottom:12px}.gd-cta p{font-size:17px;color:#ffffffbf;margin-bottom:32px}.gd-cta-actions{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}@media (max-width: 1024px){.gd-hero-inner{grid-template-columns:1fr}.gd-hero-stats{grid-template-columns:repeat(4,1fr)}.gd-features-grid,.gd-screenshots,.messenger-platforms{grid-template-columns:repeat(2,1fr)}.gd-editions-grid{grid-template-columns:1fr}.gd-tech-grid{grid-template-columns:repeat(2,1fr)}.gd-usecases{grid-template-columns:1fr}}@media (max-width: 768px){.gd-features-grid,.gd-screenshots,.messenger-platforms{grid-template-columns:1fr}.gd-hero-stats{grid-template-columns:repeat(2,1fr)}.gd-tech-grid{grid-template-columns:1fr}.cmd-item{flex-direction:column;gap:4px}.cmd-code{min-width:unset}} diff --git a/backend/src/main/resources/static/assets/Home-BC38QtTl.js b/backend/src/main/resources/static/assets/Home-BC38QtTl.js new file mode 100644 index 0000000..a1a23fc --- /dev/null +++ b/backend/src/main/resources/static/assets/Home-BC38QtTl.js @@ -0,0 +1,6 @@ +import{r as i,j as s,R as u,L as l}from"./index-ChpGil2q.js";import{a as p}from"./index-DcNlVx-A.js";const n=[{title:`AI ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ +์ž์œจ ์šด์˜ ํ”Œ๋žซํผ`,sub:"GUARDiA ITSM โ€” ๋ฉ”์‹ ์ € ํ•œ ์ค„๋กœ 1,000๊ฐœ ๊ด€๊ณต์„œ ์ธํ”„๋ผ๋ฅผ ์ž๋™ํ™”",cta:{label:"GUARDiA ์•Œ์•„๋ณด๊ธฐ",path:"/solution/guardia"},badge:"NEW",bg:"slide-1"},{title:`๊ณต๊ณต๊ธฐ๊ด€ ์ „๋ฌธ +IT ์†”๋ฃจ์…˜ ๊ธฐ์—…`,sub:"20๋…„ ๊ฒฝํ—˜์˜ ์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ์ด ์ตœ์ฒจ๋‹จ AI ๊ธฐ์ˆ ๋กœ ์—ฌ๋Ÿฌ๋ถ„๊ณผ ํ•จ๊ป˜ํ•ฉ๋‹ˆ๋‹ค",cta:{label:"ํšŒ์‚ฌ์†Œ๊ฐœ ๋ณด๊ธฐ",path:"/company/greeting"},badge:"",bg:"slide-2"},{title:`์—์ด์ „ํŠธ๋ฆฌ์Šค +์ž๋™ํ™” ํ˜์‹ `,sub:"๋Œ€์ƒ ์„œ๋ฒ„์— ์†Œํ”„ํŠธ์›จ์–ด ์„ค์น˜ ์—†์ด SSH๋งŒ์œผ๋กœ ๋ ˆ๊ฑฐ์‹œ ์ธํ”„๋ผ๋ฅผ ๊ด€๋ฆฌ",cta:{label:"๋„์ž… ๋ฌธ์˜",path:"/support/contact"},badge:"",bg:"slide-3"}],b=[{icon:"๐Ÿค–",title:"AI ์ž๋™ํ™”",desc:"GUARDiA ITSM ํ”Œ๋žซํผ์œผ๋กœ ๋ ˆ๊ฑฐ์‹œ ์ธํ”„๋ผ ์šด์˜์„ ์™„์ „ ์ž๋™ํ™”",path:"/solution/guardia",color:"var(--primary)"},{icon:"๐Ÿ—๏ธ",title:"SI ๊ตฌ์ถ•",desc:"๊ณต๊ณต๊ธฐ๊ด€ ์ •๋ณดํ™”์‚ฌ์—… ์‹œ์Šคํ…œ ํ†ตํ•ฉ ๋ฐ ๋งž์ถคํ˜• ๊ฐœ๋ฐœ",path:"/business/reference",color:"var(--accent)"},{icon:"๐Ÿ’ผ",title:"ERPยทCRMยทBI",desc:"๊ธฐ์—… ๊ฒฝ์˜ ํšจ์œจํ™”๋ฅผ ์œ„ํ•œ ํ†ตํ•ฉ ์†”๋ฃจ์…˜ ํŒจํ‚ค์ง€",path:"/solution/erp",color:"#10B981"}],v=[{icon:"๐Ÿ’ฌ",label:"ChatOps",desc:"๋ฉ”์‹ ์ € ๋ช…๋ น์œผ๋กœ ์ธํ”„๋ผ ์ œ์–ด"},{icon:"๐Ÿ”ง",label:"์—์ด์ „ํŠธ๋ฆฌ์Šค",desc:"SSH๋งŒ์œผ๋กœ ์—์ด์ „ํŠธ ์„ค์น˜ ์—†์Œ"},{icon:"๐Ÿ“Š",label:"ํ†ตํ•ฉ ITSM",desc:"SRยท์ธ์‹œ๋˜ํŠธยท๋ณ€๊ฒฝยทSLA ํ†ตํ•ฉ"},{icon:"๐Ÿ”’",label:"์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ๋ณด์•ˆ",desc:"MFAยทPAMยทZero Trust"}],g=[{value:"1,000+",label:"๊ด€๋ฆฌ ๊ฐ€๋Šฅ ๊ธฐ๊ด€ ์ˆ˜"},{value:"99.9%",label:"์‹œ์Šคํ…œ ๊ฐ€์šฉ์„ฑ"},{value:"70%",label:"SR ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ ๋‹จ์ถ•"},{value:"20๋…„+",label:"IT ์‚ฌ์—… ๊ฒฝํ—˜"}];function w(){const[d,t]=i.useState(0),[r,o]=i.useState(!1),[h,x]=i.useState([]),m=i.useRef(null);i.useEffect(()=>{if(!r)return m.current=setInterval(()=>t(e=>(e+1)%n.length),5e3),()=>clearInterval(m.current)},[r]),i.useEffect(()=>{p.get("/api/news?size=4").then(e=>x(e.data.content||[])).catch(()=>{})},[]);const j=()=>t(e=>(e-1+n.length)%n.length),N=()=>t(e=>(e+1)%n.length),c=n[d];return s.jsxs("main",{id:"main-content",children:[s.jsxs("section",{className:`hero hero-${d}`,onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),"aria-label":"๋ฉ”์ธ ์Šฌ๋ผ์ด๋”",children:[s.jsx("div",{className:"hero-overlay"}),s.jsxs("div",{className:"hero-content container",children:[c.badge&&s.jsx("span",{className:"hero-badge",children:c.badge}),s.jsx("h1",{className:"hero-title",children:c.title.split(` +`).map((e,a)=>s.jsxs(u.Fragment,{children:[e,as.jsx("button",{className:`hero-dot ${a===d?"active":""}`,onClick:()=>t(a),"aria-label":`์Šฌ๋ผ์ด๋“œ ${a+1}`},a))}),s.jsx("button",{onClick:N,"aria-label":"๋‹ค์Œ ์Šฌ๋ผ์ด๋“œ",className:"hero-arrow",children:"โ€บ"}),s.jsx("button",{onClick:()=>o(e=>!e),className:"hero-pause","aria-label":r?"์žฌ์ƒ":"์ผ์‹œ์ •์ง€",children:r?"โ–ถ":"โธ"})]}),s.jsxs("div",{className:"hero-scroll-hint","aria-hidden":"true",children:[s.jsx("span",{children:"SCROLL"}),s.jsx("div",{className:"scroll-line"})]})]}),s.jsx("section",{className:"section section-business",children:s.jsxs("div",{className:"container",children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Our Business"}),s.jsxs("h2",{className:"section-title",children:["๊ธฐ์—…๊ณผ ๊ธฐ๊ด€์„ ์œ„ํ•œ",s.jsx("br",{}),s.jsx("em",{children:"๋งž์ถคํ˜• IT ์†”๋ฃจ์…˜"})]}),s.jsx("div",{className:"divider"})]}),s.jsx("div",{className:"business-grid",children:b.map((e,a)=>s.jsxs(l,{to:e.path,className:"business-card",children:[s.jsx("div",{className:"business-icon",style:{background:e.color+"18",color:e.color},children:e.icon}),s.jsx("h3",{className:"business-title",children:e.title}),s.jsx("p",{className:"business-desc",children:e.desc}),s.jsx("span",{className:"business-more",style:{color:e.color},children:"์ž์„ธํžˆ ๋ณด๊ธฐ โ†’"})]},a))})]})}),s.jsx("section",{className:"section section-guardia",children:s.jsx("div",{className:"container",children:s.jsxs("div",{className:"guardia-inner",children:[s.jsxs("div",{className:"guardia-text",children:[s.jsx("span",{className:"section-label",children:"๋Œ€ํ‘œ ์†”๋ฃจ์…˜"}),s.jsxs("h2",{className:"section-title",style:{textAlign:"left"},children:[s.jsx("em",{children:"GUARDiA ITSM"}),s.jsx("br",{}),"AI ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ ์ž์œจ ์šด์˜"]}),s.jsx("div",{className:"divider divider-left"}),s.jsx("p",{className:"guardia-desc",children:"1,000๊ฐœ ์ด์ƒ์˜ ๊ด€๊ณต์„œ ๋ ˆ๊ฑฐ์‹œ ์ธํ”„๋ผ๋ฅผ ๋ฉ”์‹ ์ € ํ•œ ์ค„ ๋ช…๋ น์œผ๋กœ ์ œ์–ดํ•˜๋Š” ์˜จํ”„๋ ˆ๋ฏธ์Šค AI ChatOps ํ”Œ๋žซํผ. ์—์ด์ „ํŠธ ์„ค์น˜ ์—†์ด SSH/SFTP๋งŒ์œผ๋กœ ๋ฐฐํฌยท์šด์˜์„ ์™„์ „ ์ž๋™ํ™”ํ•ฉ๋‹ˆ๋‹ค."}),s.jsx("div",{className:"guardia-features",children:v.map((e,a)=>s.jsxs("div",{className:"guardia-feature",children:[s.jsx("span",{className:"feature-icon",children:e.icon}),s.jsxs("div",{children:[s.jsx("strong",{children:e.label}),s.jsx("p",{children:e.desc})]})]},a))}),s.jsxs("div",{className:"guardia-actions",children:[s.jsx(l,{to:"/solution/guardia",className:"btn btn-primary",children:"GUARDiA ์ƒ์„ธ๋ณด๊ธฐ"}),s.jsx(l,{to:"/support/contact",className:"btn btn-outline",children:"๋„์ž… ๋ฌธ์˜"})]})]}),s.jsx("div",{className:"guardia-visual",children:s.jsxs("div",{className:"guardia-mockup",children:[s.jsxs("div",{className:"mockup-bar",children:[s.jsx("span",{}),s.jsx("span",{}),s.jsx("span",{})]}),s.jsx("div",{className:"mockup-content",children:s.jsxs("div",{className:"mockup-chat",children:[s.jsxs("div",{className:"chat-msg bot",children:[s.jsx("span",{className:"chat-name",children:"GUARDiA Bot"}),s.jsx("p",{children:"์•ˆ๋…•ํ•˜์„ธ์š”! ๋ฌด์—‡์„ ๋„์™€๋“œ๋ฆด๊นŒ์š”?"})]}),s.jsx("div",{className:"chat-msg user",children:s.jsx("p",{children:"/deploy web-server-01"})}),s.jsx("div",{className:"chat-msg bot",children:s.jsxs("p",{children:["โœ… web-server-01 ๋ฐฐํฌ ์™„๋ฃŒ",s.jsx("br",{}),"ํ—ฌ์Šค์ฒดํฌ: ์ •์ƒ | ์†Œ์š”: 42์ดˆ"]})}),s.jsx("div",{className:"chat-msg user",children:s.jsx("p",{children:"/status"})}),s.jsx("div",{className:"chat-msg bot",children:s.jsxs("p",{children:["๐Ÿ“Š ์‹œ์Šคํ…œ ํ˜„ํ™ฉ",s.jsx("br",{}),"SR ์ฒ˜๋ฆฌ์ค‘: 3๊ฑด | SLA ์ค€์ˆ˜์œจ: 98.2%",s.jsx("br",{}),"์„œ๋ฒ„ ์ด์ƒ: 0๊ฑด โœ…"]})})]})})]})})]})})}),s.jsx("section",{className:"section-kpi",children:s.jsx("div",{className:"container",children:s.jsx("div",{className:"kpi-grid",children:g.map((e,a)=>s.jsxs("div",{className:"kpi-item",children:[s.jsx("div",{className:"kpi-value",children:e.value}),s.jsx("div",{className:"kpi-label",children:e.label})]},a))})})}),s.jsx("section",{className:"section section-news",children:s.jsxs("div",{className:"container",children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Latest News"}),s.jsxs("h2",{className:"section-title",children:["์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ",s.jsx("em",{children:"์†Œ์‹"})]}),s.jsx("div",{className:"divider"})]}),s.jsx("div",{className:"news-grid",children:h.length>0?h.map(e=>s.jsxs(l,{to:`/news/newsroom/${e.id}`,className:"news-card card",children:[s.jsxs("div",{className:"news-card-body",children:[s.jsx("span",{className:"badge badge-accent",children:e.category}),s.jsx("h3",{className:"news-title",children:e.title}),s.jsx("p",{className:"news-summary",children:e.summary})]}),s.jsxs("div",{className:"news-card-footer",children:[s.jsx("span",{className:"news-date",children:e.createdAt?new Date(e.createdAt).toLocaleDateString("ko-KR"):""}),s.jsx("span",{className:"news-more",children:"๋”๋ณด๊ธฐ โ†’"})]})]},e.id)):Array.from({length:4}).map((e,a)=>s.jsxs("div",{className:"news-card card skeleton",children:[s.jsx("div",{className:"skel-line",style:{width:"30%",height:"16px"}}),s.jsx("div",{className:"skel-line",style:{width:"90%",height:"20px",marginTop:"8px"}}),s.jsx("div",{className:"skel-line",style:{width:"70%",height:"14px",marginTop:"6px"}})]},a))}),s.jsx("div",{style:{textAlign:"center",marginTop:"40px"},children:s.jsx(l,{to:"/news/newsroom",className:"btn btn-outline",children:"๋ชจ๋“  ์†Œ์‹ ๋ณด๊ธฐ"})})]})}),s.jsx("section",{className:"section-cta",children:s.jsx("div",{className:"container",children:s.jsxs("div",{className:"cta-inner",children:[s.jsxs("div",{className:"cta-text",children:[s.jsx("h2",{children:"GUARDiA ITSM ๋„์ž…์„ ๊ฒ€ํ† ํ•˜๊ณ  ๊ณ„์‹ ๊ฐ€์š”?"}),s.jsx("p",{children:"์ „๋ฌธ ์ปจ์„คํ„ดํŠธ๊ฐ€ ๊ท€ ๊ธฐ๊ด€ ํ™˜๊ฒฝ์— ๋งž๋Š” ์ตœ์ ์˜ ๋ฐฉ์•ˆ์„ ์ œ์•ˆํ•ด ๋“œ๋ฆฝ๋‹ˆ๋‹ค."})]}),s.jsxs("div",{className:"cta-actions",children:[s.jsx(l,{to:"/support/contact",className:"btn btn-white btn-lg",children:"๋ฌด๋ฃŒ ์ƒ๋‹ด ์‹ ์ฒญ"}),s.jsx(l,{to:"/solution/guardia",className:"btn btn-outline btn-lg",style:{color:"#fff",borderColor:"rgba(255,255,255,.5)"},children:"์ œํ’ˆ ์†Œ๊ฐœ์„œ ๋‹ค์šด๋กœ๋“œ"})]})]})})})]})}export{w as default}; diff --git a/backend/src/main/resources/static/assets/Home-jagO1aR4.css b/backend/src/main/resources/static/assets/Home-jagO1aR4.css new file mode 100644 index 0000000..9ede489 --- /dev/null +++ b/backend/src/main/resources/static/assets/Home-jagO1aR4.css @@ -0,0 +1 @@ +.hero{position:relative;height:100vh;min-height:600px;display:flex;align-items:center;overflow:hidden;background:linear-gradient(135deg,#0a0f24,#0d2463,#0a1a4a);transition:background var(--slow)}.hero-0{background:linear-gradient(135deg,#0a0f24,#0d2463 60%,#0051a2)}.hero-1{background:linear-gradient(135deg,#1a0f3a,#2d1b69 60%,#0051a2)}.hero-2{background:linear-gradient(135deg,#001a3a,#003070 60%,#00a3e0)}.hero:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.03'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")}.hero-overlay{position:absolute;top:0;right:0;bottom:0;left:0;background:linear-gradient(to right,#00000080,#0000001a)}.hero-content{position:relative;z-index:2;max-width:700px}.hero-badge{display:inline-block;background:var(--accent);color:#fff;font-size:12px;font-weight:700;letter-spacing:2px;padding:4px 12px;border-radius:20px;margin-bottom:20px;animation:fadeUp .5s ease}.hero-title{font-size:clamp(36px,5.5vw,64px);font-weight:900;color:#fff;line-height:1.15;margin-bottom:20px;animation:fadeUp .5s .1s ease both}.hero-sub{font-size:clamp(16px,2vw,20px);color:#fffc;margin-bottom:40px;line-height:1.6;animation:fadeUp .5s .2s ease both}.hero-actions{display:flex;gap:16px;flex-wrap:wrap;animation:fadeUp .5s .3s ease both}.hero-btn-contact{border-color:#ffffff80;color:#fff!important}.hero-btn-contact:hover{background:#ffffff26!important;color:#fff!important}.hero-controls{position:absolute;bottom:40px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:12px;z-index:3}.hero-arrow{width:36px;height:36px;border-radius:50%;background:#ffffff26;color:#fff;font-size:20px;display:flex;align-items:center;justify-content:center;transition:background var(--fast)}.hero-arrow:hover{background:#ffffff4d}.hero-dots{display:flex;gap:8px}.hero-dot{width:8px;height:8px;border-radius:50%;background:#fff6;transition:all var(--mid)}.hero-dot.active{background:#fff;width:24px;border-radius:4px}.hero-pause{width:32px;height:32px;border-radius:50%;background:#ffffff1a;color:#ffffffb3;font-size:12px;display:flex;align-items:center;justify-content:center}.hero-scroll-hint{position:absolute;right:40px;bottom:40px;display:flex;flex-direction:column;align-items:center;gap:8px;color:#ffffff80;font-size:10px;letter-spacing:2px}.scroll-line{width:1px;height:60px;background:linear-gradient(to bottom,rgba(255,255,255,.5),transparent);animation:scrollPulse 1.5s ease infinite}@keyframes scrollPulse{0%,to{opacity:.5;transform:scaleY(1)}50%{opacity:1;transform:scaleY(1.1)}}.business-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px}.business-card{background:var(--white);border-radius:var(--radius-lg);padding:40px 32px;border:1px solid var(--gray-200);transition:all var(--mid) var(--ease);display:flex;flex-direction:column;gap:12px}.business-card:hover{box-shadow:var(--shadow-lg);transform:translateY(-6px);border-color:transparent}.business-icon{width:64px;height:64px;border-radius:var(--radius);font-size:28px;display:flex;align-items:center;justify-content:center}.business-title{font-size:22px;font-weight:700;color:var(--gray-900)}.business-desc{font-size:15px;color:var(--gray-600);line-height:1.7;flex:1}.business-more{font-size:14px;font-weight:600}.section-guardia{background:var(--gray-50)}.guardia-inner{display:grid;grid-template-columns:1fr 1fr;gap:80px;align-items:center}.guardia-desc{font-size:16px;color:var(--gray-600);line-height:1.8;margin:20px 0 28px}.guardia-features{display:flex;flex-direction:column;gap:16px;margin-bottom:32px}.guardia-feature{display:flex;align-items:flex-start;gap:16px;padding:14px 18px;background:var(--white);border-radius:var(--radius);border:1px solid var(--gray-200)}.feature-icon{font-size:24px;flex-shrink:0}.guardia-feature strong{display:block;font-size:15px;color:var(--gray-900)}.guardia-feature p{font-size:13px;color:var(--gray-600);margin-top:2px}.guardia-actions{display:flex;gap:12px;flex-wrap:wrap}.guardia-mockup{background:#1e1e2e;border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-lg);border:1px solid rgba(255,255,255,.1)}.mockup-bar{background:#2d2d3f;padding:10px 16px;display:flex;gap:6px}.mockup-bar span{width:12px;height:12px;border-radius:50%;background:#ff5f56}.mockup-bar span:nth-child(2){background:#ffbd2e}.mockup-bar span:nth-child(3){background:#27c93f}.mockup-content{padding:20px}.mockup-chat{display:flex;flex-direction:column;gap:12px}.chat-msg{max-width:80%}.chat-msg.bot{align-self:flex-start}.chat-msg.user{align-self:flex-end}.chat-name{font-size:11px;color:var(--accent);font-weight:700;display:block;margin-bottom:4px}.chat-msg p{padding:10px 14px;border-radius:12px;font-size:13px;line-height:1.5}.chat-msg.bot p{background:#2d2d3f;color:#ffffffe6}.chat-msg.user p{background:var(--primary);color:#fff}.section-kpi{background:var(--primary);padding:60px 0}.kpi-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0}.kpi-item{text-align:center;padding:24px;border-right:1px solid rgba(255,255,255,.2)}.kpi-item:last-child{border-right:none}.kpi-value{font-size:clamp(32px,4vw,48px);font-weight:900;color:#fff}.kpi-label{font-size:14px;color:#ffffffbf;margin-top:6px}.news-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px}.news-card{display:flex;flex-direction:column}.news-card-body{padding:24px;flex:1;display:flex;flex-direction:column;gap:10px}.news-title{font-size:16px;font-weight:700;color:var(--gray-900);line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.news-summary{font-size:13px;color:var(--gray-600);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;flex:1}.news-card-footer{display:flex;justify-content:space-between;align-items:center;padding:12px 24px;border-top:1px solid var(--gray-100);font-size:12px}.news-date{color:var(--gray-400)}.news-more{color:var(--primary);font-weight:600}.skeleton{animation:pulse 1.5s ease infinite}@keyframes pulse{0%,to{opacity:.6}50%{opacity:1}}.skel-line{background:var(--gray-200);border-radius:4px}.section-cta{background:linear-gradient(135deg,var(--secondary),var(--primary-dark));padding:80px 0}.cta-inner{display:flex;align-items:center;justify-content:space-between;gap:40px;flex-wrap:wrap}.cta-text h2{font-size:28px;font-weight:800;color:#fff}.cta-text p{color:#ffffffbf;margin-top:8px;font-size:16px}.cta-actions{display:flex;gap:16px;flex-wrap:wrap;flex-shrink:0}@media (max-width: 1024px){.business-grid{grid-template-columns:repeat(2,1fr)}.guardia-inner{grid-template-columns:1fr;gap:40px}.kpi-grid,.news-grid{grid-template-columns:repeat(2,1fr)}}@media (max-width: 768px){.hero-title{font-size:32px}.hero-sub{font-size:15px}.hero-scroll-hint{display:none}.business-grid{grid-template-columns:1fr}.kpi-grid{grid-template-columns:repeat(2,1fr)}.news-grid{grid-template-columns:1fr}.cta-inner{flex-direction:column;text-align:center}.cta-actions{justify-content:center}} diff --git a/backend/src/main/resources/static/assets/NewsPage-BgXQ2CUT.css b/backend/src/main/resources/static/assets/NewsPage-BgXQ2CUT.css new file mode 100644 index 0000000..ad2129b --- /dev/null +++ b/backend/src/main/resources/static/assets/NewsPage-BgXQ2CUT.css @@ -0,0 +1 @@ +.notice-back{font-size:14px;color:var(--primary);margin-bottom:24px;display:inline-flex;align-items:center;gap:4px;cursor:pointer;background:none;border:none}.news-cat-badge{display:inline-block;padding:4px 12px;border-radius:12px;font-size:12px;font-weight:700;margin-bottom:12px}.news-cat-badge.hot{background:#ef44441f;color:var(--danger)}.news-main{padding:40px;cursor:pointer;background:linear-gradient(135deg,var(--secondary),var(--primary-dark));border:none;margin-bottom:0}.news-main:hover{transform:none;box-shadow:var(--shadow-lg)}.news-main-title{font-size:26px;font-weight:900;color:#fff;margin-bottom:16px;line-height:1.35}.news-main-summary{font-size:15px;color:#ffffffb3;line-height:1.8;margin-bottom:16px;max-width:640px}.news-date{font-size:12px;color:#ffffff80}.news-card{padding:28px;cursor:pointer;display:flex;flex-direction:column}.news-card-title{font-size:15px;font-weight:700;color:var(--gray-900);margin-bottom:10px;line-height:1.5;flex:1}.news-card-summary{font-size:13px;color:var(--gray-600);line-height:1.7;margin-bottom:12px;flex:1}.news-date{font-size:12px;color:var(--gray-400)}.blog-card{padding:28px;display:flex;flex-direction:column}.blog-tag{display:inline-block;padding:4px 12px;border-radius:12px;font-size:12px;font-weight:700;margin-bottom:14px;align-self:flex-start}.blog-title{font-size:16px;font-weight:700;color:var(--gray-900);line-height:1.5;margin-bottom:12px}.blog-summary{font-size:13px;color:var(--gray-600);line-height:1.7;flex:1;margin-bottom:16px}.blog-meta{display:flex;gap:16px;font-size:12px;color:var(--gray-400);margin-bottom:16px}.blog-read-btn{padding:10px 20px;background:var(--primary-light);color:var(--primary);border-radius:8px;font-size:14px;font-weight:700;border:none;cursor:pointer;transition:all var(--fast);text-align:center}.blog-read-btn:hover{background:var(--primary);color:#fff} diff --git a/backend/src/main/resources/static/assets/NewsPage-mgytOZhS.js b/backend/src/main/resources/static/assets/NewsPage-mgytOZhS.js new file mode 100644 index 0000000..2f84697 --- /dev/null +++ b/backend/src/main/resources/static/assets/NewsPage-mgytOZhS.js @@ -0,0 +1,10 @@ +import{j as e,b as o,a as n,r as h,N as x}from"./index-ChpGil2q.js";/* empty css */const p=[{path:"/news/newsroom",label:"๋‰ด์Šค๋ฃธ"},{path:"/news/blog",label:"๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ"}];function d({title:a}){return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"page-hero",children:e.jsxs("div",{className:"container",children:[e.jsx("span",{className:"section-label",children:"News"}),e.jsx("h1",{className:"page-hero-title",children:a})]})}),e.jsx("nav",{className:"sub-nav",children:e.jsx("div",{className:"container",children:p.map(t=>e.jsx(x,{to:t.path,className:({isActive:i})=>"sub-nav-item"+(i?" active":""),children:t.label},t.path))})})]})}const l=[{id:1,cat:"์ œํ’ˆ ์ถœ์‹œ",date:"2026.05.15",title:"GUARDiA ITSM v2.0 ์ •์‹ ์ถœ์‹œ โ€” AI ChatOps ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ํ”Œ๋žซํผ",summary:"๋ฉ”์‹ ์ € ํ•œ ์ค„ ๋ช…๋ น์œผ๋กœ 1,000๊ฐœ+ ๊ณต๊ณต๊ธฐ๊ด€ ๋ ˆ๊ฑฐ์‹œ ์ธํ”„๋ผ๋ฅผ ์ž๋™ ์šด์˜ํ•˜๋Š” GUARDiA ITSM v2.0์ด ์ •์‹ ์ถœ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์‹ ๊ทœ ๊ธฐ๋Šฅ์œผ๋กœ AI ์ž์—ฐ์–ด ๋ช…๋ น, ์—์ด์ „ํŠธ๋ฆฌ์Šค ๋ฐฐํฌ ์—”์ง„, ๋ฉ€ํ‹ฐํ…Œ๋„ŒํŠธ ์ง€์›์ด ์ถ”๊ฐ€๋์Šต๋‹ˆ๋‹ค.",content:`GUARDiA ITSM v2.0์€ ๊ณต๊ณต๊ธฐ๊ด€์˜ ๋ ˆ๊ฑฐ์‹œ IT ์ธํ”„๋ผ ์šด์˜ ์ž๋™ํ™”๋ฅผ ์œ„ํ•œ AI ๊ธฐ๋ฐ˜ ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. + +์ฃผ์š” ์‹ ๊ธฐ๋Šฅ: +- AI ChatOps: ๋ฉ”์‹ ์ € ์ž์—ฐ์–ด ๋ช…๋ น โ†’ Ollama LLM ํŒŒ์‹ฑ โ†’ ์ž๋™ ์‹คํ–‰ +- ์—์ด์ „ํŠธ๋ฆฌ์Šค ๋ฐฐํฌ: SSH/SFTP๋งŒ์œผ๋กœ WAS ๋ฐฐํฌยท๋กค๋ฐฑ ์ž๋™ํ™” +- ๋ฉ€ํ‹ฐํ…Œ๋„ŒํŠธ: 1,000๊ฐœ+ ๊ธฐ๊ด€ ๋™์‹œ ๊ด€๋ฆฌ +- GS์ธ์ฆ 1๋“ฑ๊ธ‰ ์‹ ์ฒญ ์™„๋ฃŒ + +์ž์„ธํ•œ ์‚ฌํ•ญ์€ GUARDiA ์†Œ๊ฐœ ํŽ˜์ด์ง€๋ฅผ ์ฐธ์กฐํ•ด ์ฃผ์‹ญ์‹œ์˜ค.`,hot:!0},{id:2,cat:"์ˆ˜์ฃผ ์†Œ์‹",date:"2026.04.20",title:"์‚ผ์„ฑ์ „์ž ์ฐจ์„ธ๋Œ€ CRM ์‹œ์Šคํ…œ DB ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ”„๋กœ์ ํŠธ ์ˆ˜์ฃผ",summary:"(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ์ด ์‚ผ์„ฑ์ „์ž ์ฐจ์„ธ๋Œ€ CRM ๊ตฌ์ถ• ํ”„๋กœ์ ํŠธ์˜ DB Migration/DA/ํŠœ๋‹์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค. EDB PostgreSQL ํ™˜๊ฒฝ์œผ๋กœ์˜ ์ „ํ™˜์„ ํฌํ•จํ•œ ๋Œ€๊ทœ๋ชจ DB ํ˜„๋Œ€ํ™” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.",content:"์‚ผ์„ฑ์ „์ž์™€์˜ ๋‘ ๋ฒˆ์งธ ํ˜‘๋ ฅ ํ”„๋กœ์ ํŠธ๋กœ, DB ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐ ์„ฑ๋Šฅ ํŠœ๋‹์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.",hot:!1},{id:3,cat:"๊ธฐ์ˆ  ์ธ์ฆ",date:"2026.03.10",title:"GUARDiA ITSM GS์ธ์ฆ 1๋“ฑ๊ธ‰ ์‹ ์ฒญ ์™„๋ฃŒ โ€” TTA ์‹ฌ์‚ฌ ์˜ˆ์ •",summary:"GUARDiA ITSM์ด ํ•œ๊ตญ์ •๋ณดํ†ต์‹ ๊ธฐ์ˆ ํ˜‘ํšŒ(TTA)์— GS์ธ์ฆ 1๋“ฑ๊ธ‰์„ ์‹ ์ฒญํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ์ ํ•ฉ์„ฑ, ์‹ ๋ขฐ์„ฑ, ์‚ฌ์šฉ์„ฑ, ๋ณด์•ˆ์„ฑ ๋“ฑ ISO/IEC 25010 ๊ธฐ์ค€ 8๋Œ€ ํ’ˆ์งˆ ํŠน์„ฑ ์‹ฌ์‚ฌ๋ฅผ ์•ž๋‘๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.",content:"GS์ธ์ฆ ์‹ฌ์‚ฌ๋Š” 2026๋…„ 9์›” ์˜ˆ์ •์ด๋ฉฐ, 1๋“ฑ๊ธ‰ ์ทจ๋“ ์‹œ ์กฐ๋‹ฌ์ฒญ ๋‚˜๋ผ์žฅํ„ฐ ์šฐ์„  ๋“ฑ์žฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.",hot:!1},{id:4,cat:"์ˆ˜์ฃผ ์†Œ์‹",date:"2026.02.15",title:"๊ตญ๋ฏผ์—ฐ๊ธˆ๊ณต๋‹จ ์ฐจ์„ธ๋Œ€ ์‹œ์Šคํ…œ ๊ตฌ์ถ• โ€” AA ์—ญํ•  ์ˆ˜ํ–‰",summary:"๊ตญ๋ฏผ์—ฐ๊ธˆ๊ณต๋‹จ ์ฐจ์„ธ๋Œ€ ์‹œ์Šคํ…œ ๊ตฌ์ถ• ํ”„๋กœ์ ํŠธ์— Application Architect(AA)๋กœ ์ฐธ์—ฌํ•ฉ๋‹ˆ๋‹ค. JSP/Java, Nexacro, Spring ๊ธฐ๋ฐ˜์˜ ๋Œ€๊ทœ๋ชจ ๊ณต๊ณต๊ธฐ๊ด€ ์‹œ์Šคํ…œ ๊ตฌ์ถ•์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.",content:"๊ตญ๋ฏผ์—ฐ๊ธˆ๊ด€๋ฆฌ๊ณต๋‹จ์˜ ์ฐจ์„ธ๋Œ€ ์‹œ์Šคํ…œ์€ ์ˆ˜์ฒœ๋งŒ ๊ฐ€์ž…์ž์˜ ์—ฐ๊ธˆ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์œผ๋กœ, CI/CD ํŒŒ์ดํ”„๋ผ์ธ ๊ธฐ๋ฐ˜์˜ ํ˜„๋Œ€์ ์ธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค.",hot:!1},{id:5,cat:"๊ธฐ์—… ์†Œ์‹",date:"2025.12.01",title:"2025๋…„ ์‚ฌ์—…์‹ค์  โ€” ์—ฐ๊ฐ„ ํ”„๋กœ์ ํŠธ 10๊ฑด ์„ฑ๊ณต ์ˆ˜ํ–‰",summary:"2025๋…„ ํ•œ ํ•ด ๋™์•ˆ ์‚ผ์„ฑ์ „์ž, ์„œ์šธ์‹ ์šฉ๋ณด์ฆ์žฌ๋‹จ, ํ—Œ๋ฒ•์žฌํŒ์†Œ ๋“ฑ 10๊ฐœ ์ฃผ์š” ํ”„๋กœ์ ํŠธ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋งค์ถœ์€ ์ „๋…„ ๋Œ€๋น„ 25% ์„ฑ์žฅํ•˜์˜€์Šต๋‹ˆ๋‹ค.",content:"์ฐฝ๋ฆฝ ์ด๋ž˜ ์ตœ๋Œ€ ์„ฑ๊ณผ๋ฅผ ๊ธฐ๋กํ•œ 2025๋…„ ์‚ฌ์—…์‹ค์ ์„ ๊ณต์œ ๋“œ๋ฆฝ๋‹ˆ๋‹ค.",hot:!1},{id:6,cat:"ํŒŒํŠธ๋„ˆ์‹ญ",date:"2025.09.10",title:"Tibero ๊ณต์‹ ํŒŒํŠธ๋„ˆ์‚ฌ ๋“ฑ๋ก โ€” ๊ณต๊ณต๊ธฐ๊ด€ DB ์ „ํ™˜ ์†”๋ฃจ์…˜ ๊ฐ•ํ™”",summary:"๊ตญ์‚ฐ DBMS Tibero์˜ ๊ณต์‹ ํŒŒํŠธ๋„ˆ์‚ฌ๋กœ ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Oracle์—์„œ Tibero๋กœ์˜ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐ ๊ณต๊ณต๊ธฐ๊ด€ DB ํ˜„๋Œ€ํ™” ์‚ฌ์—…์„ ๊ณต๋™์œผ๋กœ ์ถ”์ง„ํ•ฉ๋‹ˆ๋‹ค.",content:"๊ณต๊ณต๊ธฐ๊ด€์˜ Oracle ๋ผ์ด์„ ์Šค ์ ˆ๊ฐ์„ ์œ„ํ•œ Tibero ์ „ํ™˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์ „๋ฌธ์ ์œผ๋กœ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.",hot:!1}];function c(){const[a,t]=h.useState(null),i=l.find(s=>s.id===a);return e.jsxs("main",{id:"main-content",className:"inner-page",children:[e.jsx(d,{title:"๋‰ด์Šค๋ฃธ"}),e.jsx("section",{className:"section",children:e.jsx("div",{className:"container",children:i?e.jsxs("div",{style:{maxWidth:"760px",margin:"0 auto"},children:[e.jsx("button",{className:"notice-back",onClick:()=>t(null),children:"โ† ๋‰ด์Šค ๋ชฉ๋ก"}),e.jsxs("div",{className:"news-detail card",style:{padding:"40px"},children:[e.jsx("span",{className:"news-cat-badge",style:{background:"var(--primary-light)",color:"var(--primary)"},children:i.cat}),e.jsx("h2",{style:{fontSize:"24px",fontWeight:"900",margin:"16px 0 8px",lineHeight:"1.4"},children:i.title}),e.jsx("p",{style:{fontSize:"13px",color:"var(--gray-400)",marginBottom:"32px"},children:i.date}),e.jsx("div",{className:"divider divider-left",style:{marginBottom:"32px"}}),i.content.split(` +`).map((s,m)=>s.trim()?e.jsx("p",{style:{fontSize:"15px",color:"var(--gray-700)",lineHeight:"1.85",marginBottom:"16px"},children:s},m):null)]})]}):e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"news-main card",onClick:()=>t(l[0].id),children:e.jsxs("div",{className:"news-main-content",children:[e.jsxs("span",{className:"news-cat-badge hot",children:["๐Ÿ”ฅ ",l[0].cat]}),e.jsx("h2",{className:"news-main-title",children:l[0].title}),e.jsx("p",{className:"news-main-summary",children:l[0].summary}),e.jsx("span",{className:"news-date",children:l[0].date})]})}),e.jsx("div",{className:"grid-3",style:{marginTop:"24px"},children:l.slice(1).map(s=>e.jsxs("div",{className:"card news-card",onClick:()=>t(s.id),children:[e.jsx("span",{className:"news-cat-badge",style:{background:"var(--primary-light)",color:"var(--primary)"},children:s.cat}),e.jsx("h3",{className:"news-card-title",children:s.title}),e.jsx("p",{className:"news-card-summary",children:s.summary}),e.jsx("span",{className:"news-date",children:s.date})]},s.id))})]})})})]})}const j=[{id:1,tag:"AIยทLLM",date:"2026.05.20",title:"์˜จํ”„๋ ˆ๋ฏธ์Šค Ollama๋กœ ํ์‡„๋ง ChatOps ๊ตฌํ˜„ํ•˜๊ธฐ",summary:"์ธํ„ฐ๋„ท ์—†์ด ๋‚ด๋ถ€๋ง์—์„œ LLM์„ ์šด์˜ํ•˜๋Š” ๋ฐฉ๋ฒ•. Llama-3-8B ๋ชจ๋ธ์„ Ollama๋กœ ๊ตฌ๋™ํ•˜๊ณ  FastAPI์™€ ์—ฐ๋™ํ•˜๋Š” ์ „์ฒด ๊ณผ์ •์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.",readMin:12},{id:2,tag:"DevOps",date:"2026.05.10",title:"์—์ด์ „ํŠธ๋ฆฌ์Šค WAS ๋ฐฐํฌ ์ž๋™ํ™” โ€” paramiko SSH๋กœ ๋ ˆ๊ฑฐ์‹œ ์„œ๋ฒ„ ๊ด€๋ฆฌ",summary:"JEUSยทTomcat ๋“ฑ ๋ ˆ๊ฑฐ์‹œ WAS์— SSH/SFTP๋งŒ์œผ๋กœ ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ๋ฒ•. ๋ฐฑ์—…โ†’๋ฐฐํฌโ†’ํ—ฌ์Šค์ฒดํฌโ†’๋กค๋ฐฑ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌํ˜„ ์˜ˆ์ œ.",readMin:15},{id:3,tag:"๋ณด์•ˆ",date:"2026.04.28",title:"AES-256-GCM์œผ๋กœ ์„œ๋ฒ„ ์ž๊ฒฉ์ฆ๋ช…์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ•˜๋Š” ๋ฒ•",summary:"๊ณต๊ณต๊ธฐ๊ด€ ์„œ๋ฒ„ SSH ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ DB์— ์•ˆ์ „ํ•˜๊ฒŒ ์•”ํ˜ธํ™” ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•. IVยท์•”ํ˜ธ๋ฌธยทGCM Tag ๊ตฌ์กฐ ์„ค๊ณ„์™€ Python ๊ตฌํ˜„.",readMin:8},{id:4,tag:"๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค",date:"2026.04.15",title:"Oracle 19c โ†’ EDB PostgreSQL ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹ค์ „ ๊ฐ€์ด๋“œ",summary:"์‚ผ์„ฑ์ „์ž CRM ํ”„๋กœ์ ํŠธ์—์„œ ์‹ค์ œ ์ˆ˜ํ–‰ํ•œ Oracleโ†’EDB ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฒฝํ—˜ ๊ณต์œ . Smeta, ExemOne ํ™œ์šฉ SQL ๋ณ€ํ™˜ ์ „๋žต.",readMin:20},{id:5,tag:"์„ฑ๋Šฅ",date:"2026.03.25",title:"๊ณต๊ณต๊ธฐ๊ด€ ํ–‰์ •์ •๋ณด์‹œ์Šคํ…œ SQL ํŠœ๋‹ โ€” ์„œ์šธ์‹œ๋ฆฝ๋Œ€ ์‚ฌ๋ก€",summary:"๋Œ€ํ•™ํ–‰์ •์ •๋ณด์‹œ์Šคํ…œ ์„ฑ๋Šฅ ๊ฐœ์„  ํ”„๋กœ์ ํŠธ ์‹ค์ „ ์‚ฌ๋ก€. JMeter ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ์™€ Oracle ์‹คํ–‰๊ณ„ํš ๋ถ„์„์œผ๋กœ ์‘๋‹ต์‹œ๊ฐ„ 60% ๋‹จ์ถ•.",readMin:18},{id:6,tag:"์•„ํ‚คํ…์ฒ˜",date:"2026.03.10",title:"FastAPI ๋น„๋™๊ธฐ WebSocket์œผ๋กœ ์‹ค์‹œ๊ฐ„ ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์ถ•ํ•˜๊ธฐ",summary:"GUARDiA ITSM ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง ๋Œ€์‹œ๋ณด๋“œ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•. FastAPI SSE + WebSocket + React๋ฅผ ์กฐํ•ฉํ•œ ํ’€์Šคํƒ ์•„ํ‚คํ…์ฒ˜.",readMin:14}],r={"AIยทLLM":"#7c3aed",DevOps:"#0051A2",๋ณด์•ˆ:"#dc2626",๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค:"#d97706",์„ฑ๋Šฅ:"#059669",์•„ํ‚คํ…์ฒ˜:"#0891b2"};function g(){return e.jsxs("main",{id:"main-content",className:"inner-page",children:[e.jsx(d,{title:"๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ"}),e.jsx("section",{className:"section",children:e.jsxs("div",{className:"container",children:[e.jsxs("div",{className:"section-header",children:[e.jsx("span",{className:"section-label",children:"Tech Blog"}),e.jsx("h2",{className:"section-title",children:"๊ธฐ์ˆ  ์ธ์‚ฌ์ดํŠธ ๊ณต์œ "}),e.jsx("p",{className:"section-desc",children:"20๋…„ ์ด์ƒ์˜ ํ”„๋กœ์ ํŠธ ๊ฒฝํ—˜์—์„œ ์–ป์€ ๊ธฐ์ˆ  ๋…ธํ•˜์šฐ๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค"})]}),e.jsx("div",{className:"grid-3",children:j.map(a=>e.jsxs("div",{className:"card blog-card",children:[e.jsx("div",{className:"blog-tag",style:{background:r[a.tag]+"18",color:r[a.tag]},children:a.tag}),e.jsx("h3",{className:"blog-title",children:a.title}),e.jsx("p",{className:"blog-summary",children:a.summary}),e.jsxs("div",{className:"blog-meta",children:[e.jsxs("span",{children:["๐Ÿ“… ",a.date]}),e.jsxs("span",{children:["โฑ ",a.readMin,"๋ถ„ ์ฝ๊ธฐ"]})]}),e.jsx("button",{className:"blog-read-btn",onClick:()=>alert("๋ธ”๋กœ๊ทธ ์ƒ์„ธ ํŽ˜์ด์ง€๋Š” ์ค€๋น„ ์ค‘์ž…๋‹ˆ๋‹ค."),children:"์ฝ๊ธฐ โ†’"})]},a.id))})]})})]})}function u(){return e.jsxs(o,{children:[e.jsx(n,{path:"newsroom",element:e.jsx(c,{})}),e.jsx(n,{path:"blog",element:e.jsx(g,{})}),e.jsx(n,{path:"press",element:e.jsx(c,{})}),e.jsx(n,{path:"*",element:e.jsx(c,{})})]})}export{u as default}; diff --git a/backend/src/main/resources/static/assets/NotFound-KZZDVQMb.js b/backend/src/main/resources/static/assets/NotFound-KZZDVQMb.js new file mode 100644 index 0000000..b50468a --- /dev/null +++ b/backend/src/main/resources/static/assets/NotFound-KZZDVQMb.js @@ -0,0 +1 @@ +import{j as e,L as t}from"./index-ChpGil2q.js";function i(){return e.jsxs("main",{style:{paddingTop:"var(--header-h)",minHeight:"60vh",display:"flex",alignItems:"center",justifyContent:"center",flexDirection:"column",gap:"16px",textAlign:"center"},children:[e.jsx("div",{style:{fontSize:"72px"},children:"404"}),e.jsx("h1",{style:{fontSize:"24px",fontWeight:"700"},children:"ํŽ˜์ด์ง€๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"}),e.jsx("p",{style:{color:"var(--gray-600)"},children:"์š”์ฒญํ•˜์‹  ํŽ˜์ด์ง€๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ์ด๋™๋˜์—ˆ์Šต๋‹ˆ๋‹ค."}),e.jsx(t,{to:"/",className:"btn btn-primary",children:"ํ™ˆ์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ"})]})}export{i as default}; diff --git a/backend/src/main/resources/static/assets/Recruit-CbW65yqF.css b/backend/src/main/resources/static/assets/Recruit-CbW65yqF.css new file mode 100644 index 0000000..6c5d26e --- /dev/null +++ b/backend/src/main/resources/static/assets/Recruit-CbW65yqF.css @@ -0,0 +1 @@ +.notice-back{font-size:14px;color:var(--primary);margin-bottom:24px;display:inline-flex;align-items:center;gap:4px;cursor:pointer;background:none;border:none}.job-card{padding:28px;display:flex;gap:24px;align-items:flex-start;cursor:pointer}.job-card:hover{border-color:var(--primary)}.job-info{flex:1}.job-title{font-size:18px;font-weight:700;color:var(--gray-900);margin-bottom:8px}.job-desc{font-size:13px;color:var(--gray-600);line-height:1.6;margin-bottom:12px}.job-stack{display:flex;gap:6px;flex-wrap:wrap}.job-tech{padding:4px 10px;background:var(--secondary);color:var(--accent);border-radius:4px;font-size:11px;font-weight:600}.job-meta{display:flex;flex-direction:column;gap:8px;align-items:flex-end;min-width:100px}.job-meta>div{display:flex;flex-direction:column;align-items:flex-end;font-size:13px;color:var(--gray-700)}.job-meta-label{font-size:11px;color:var(--gray-400);margin-bottom:2px}.welfare-cat{font-size:18px;font-weight:800;color:var(--gray-900);margin-bottom:20px}.welfare-card{padding:28px;text-align:center}.welfare-icon{font-size:36px;margin-bottom:12px}.welfare-name{font-size:15px;font-weight:700;margin-bottom:8px;color:var(--gray-900)}.welfare-desc{font-size:13px;color:var(--gray-600);line-height:1.6}.talent-wrap{background:var(--gray-50);border-radius:16px;padding:56px;margin-top:32px}.apply-form .form-group{margin-bottom:20px}.apply-form label{display:block;font-size:13px;font-weight:600;color:var(--gray-700);margin-bottom:6px}.apply-form input,.apply-form select,.apply-form textarea{width:100%;padding:12px 14px;border:1px solid var(--gray-200);border-radius:8px;font-size:14px;font-family:inherit;transition:border-color var(--fast)}.apply-form input:focus,.apply-form select:focus,.apply-form textarea:focus{outline:none;border-color:var(--primary)}.apply-form .form-row{display:grid;grid-template-columns:1fr 1fr;gap:16px}.required{color:var(--danger)}.apply-success{text-align:center;padding:80px 40px;background:var(--gray-50);border-radius:16px}.apply-success h3{font-size:24px;font-weight:800;margin-bottom:16px}.apply-success p{font-size:15px;color:var(--gray-600);line-height:1.8}@media (max-width:768px){.job-card{flex-direction:column}.job-meta{align-items:flex-start;flex-direction:row;flex-wrap:wrap}.apply-form .form-row{grid-template-columns:1fr}} diff --git a/backend/src/main/resources/static/assets/Recruit-DlKGM6KQ.js b/backend/src/main/resources/static/assets/Recruit-DlKGM6KQ.js new file mode 100644 index 0000000..7f12a61 --- /dev/null +++ b/backend/src/main/resources/static/assets/Recruit-DlKGM6KQ.js @@ -0,0 +1 @@ +import{j as e,b as h,a as t,r as d,N as j}from"./index-ChpGil2q.js";/* empty css */const u=[{path:"/recruit/jobs",label:"์ฑ„์šฉ๊ณต๊ณ "},{path:"/recruit/welfare",label:"๋ณต๋ฆฌํ›„์ƒ"},{path:"/recruit/apply",label:"์ง€์›ํ•˜๊ธฐ"}];function r({title:a}){return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"page-hero",children:e.jsxs("div",{className:"container",children:[e.jsx("span",{className:"section-label",children:"Recruit"}),e.jsx("h1",{className:"page-hero-title",children:a}),e.jsx("p",{children:"์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ๊ณผ ํ•จ๊ป˜ AI ์ธํ”„๋ผ ํ˜์‹ ์„ ์ด๋Œ์–ด ๊ฐˆ ์ธ์žฌ๋ฅผ ๋ชจ์‹ญ๋‹ˆ๋‹ค."})]})}),e.jsx("nav",{className:"sub-nav",children:e.jsx("div",{className:"container",children:u.map(i=>e.jsx(j,{to:i.path,className:({isActive:s})=>"sub-nav-item"+(s?" active":""),children:i.label},i.path))})})]})}const o=[{id:1,title:"AI/LLM ์—”์ง€๋‹ˆ์–ด",dept:"AIํŒ€",type:"์ •๊ทœ์ง",exp:"๊ฒฝ๋ ฅ 3๋…„ ์ด์ƒ",stack:["Python","Ollama","LangChain","FastAPI"],desc:"GUARDiA ITSM์˜ ์˜จํ”„๋ ˆ๋ฏธ์Šค AI ์—”์ง„ ๊ฐœ๋ฐœ. ์ž์—ฐ์–ดโ†’๋ช…๋ น ํŒŒ์‹ฑ, LLM ํŒŒ์ธํŠœ๋‹, RAG ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ•.",deadline:"2026.06.30",hot:!0},{id:2,title:"Java ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž (Spring Boot)",dept:"๊ฐœ๋ฐœํŒ€",type:"์ •๊ทœ์ง",exp:"๊ฒฝ๋ ฅ 3๋…„ ์ด์ƒ",stack:["Java","Spring Boot","Oracle","MyBatis"],desc:"๊ณต๊ณต๊ธฐ๊ด€ SI/SM ํ”„๋กœ์ ํŠธ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ. ERPยทCRMยทํ–‰์ •์ •๋ณด์‹œ์Šคํ…œ ๊ตฌ์ถ• ๋ฐ ์œ ์ง€๋ณด์ˆ˜.",deadline:"2026.06.30",hot:!0},{id:3,title:"React ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž",dept:"๊ฐœ๋ฐœํŒ€",type:"์ •๊ทœ์ง",exp:"๊ฒฝ๋ ฅ 2๋…„ ์ด์ƒ",stack:["React","TypeScript","Vite","Chart.js"],desc:"GUARDiA ITSM ๋ฐ ๊ณ ๊ฐ์‚ฌ ํฌํ„ธ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ. ๊ณต๊ณต๊ธฐ๊ด€ ์›น์ ‘๊ทผ์„ฑ(KWCAG 2.1) ์ค€์ˆ˜ ํ•„์ˆ˜.",deadline:"2026.06.30",hot:!1},{id:4,title:"์ธํ”„๋ผ ์šด์˜ ์—”์ง€๋‹ˆ์–ด (DBA)",dept:"์šด์˜ํŒ€",type:"์ •๊ทœ์ง",exp:"๊ฒฝ๋ ฅ 3๋…„ ์ด์ƒ",stack:["Oracle","Tibero","Linux","Shell"],desc:"Oracle/Tibero DB ์„ค๊ณ„ยทํŠœ๋‹ยท์ด๊ด€. ์‚ผ์„ฑ์ „์žยท๊ตญ๋ฏผ์—ฐ๊ธˆ๊ธ‰ ๋Œ€ํ˜• DB ์šด์˜ ๊ฒฝํ—˜ ์šฐ๋Œ€.",deadline:"2026.06.15",hot:!1},{id:5,title:"PM / PL (๊ณต๊ณต SI)",dept:"PM๋ณธ๋ถ€",type:"์ •๊ทœ์ง",exp:"๊ฒฝ๋ ฅ 5๋…„ ์ด์ƒ",stack:["PMP","PMBOK","MS Project","Jira"],desc:"๊ณต๊ณต๊ธฐ๊ด€ ์ •๋ณดํ™”์‚ฌ์—… PM/PL. ํ—Œ๋ฒ•์žฌํŒ์†Œยท๊ตญ๋ฏผ์—ฐ๊ธˆยท์‹œ๋ฆฝ๋Œ€ ์ˆ˜์ค€ ํ”„๋กœ์ ํŠธ ๊ด€๋ฆฌ ๊ฒฝํ—˜ ๋ณด์œ ์ž.",deadline:"2026.06.15",hot:!1},{id:6,title:"DevOps / CIยทCD ์—”์ง€๋‹ˆ์–ด",dept:"๊ฐœ๋ฐœํŒ€",type:"์ •๊ทœ์ง",exp:"๊ฒฝ๋ ฅ 2๋…„ ์ด์ƒ",stack:["Docker","Kubernetes","Jenkins","GitHub Actions"],desc:"GUARDiA Vibe CD ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ• ๋ฐ ์šด์˜. ํ์‡„๋ง ํ™˜๊ฒฝ GitOps ๊ฒฝํ—˜ ์šฐ๋Œ€.",deadline:"์ƒ์‹œ",hot:!1}];function p(){const[a,i]=d.useState(null);if(a){const s=o.find(l=>l.id===a);return e.jsxs("main",{id:"main-content",className:"inner-page",children:[e.jsx(r,{title:"์ฑ„์šฉ๊ณต๊ณ "}),e.jsx("section",{className:"section",children:e.jsxs("div",{className:"container",style:{maxWidth:"760px"},children:[e.jsx("button",{className:"notice-back",onClick:()=>i(null),children:"โ† ๋ชฉ๋ก์œผ๋กœ"}),e.jsxs("div",{className:"job-detail-card card",style:{padding:"40px"},children:[e.jsxs("div",{className:"job-detail-header",children:[s.hot&&e.jsx("span",{className:"badge badge-new",children:"HOT"}),e.jsx("h2",{style:{fontSize:"26px",fontWeight:"900",margin:"12px 0 8px"},children:s.title}),e.jsxs("div",{style:{display:"flex",gap:"10px",flexWrap:"wrap"},children:[e.jsx("span",{className:"badge badge-primary",children:s.dept}),e.jsx("span",{className:"badge badge-accent",children:s.type}),e.jsxs("span",{style:{fontSize:"13px",color:"var(--gray-500)"},children:["๊ฒฝ๋ ฅ: ",s.exp]}),e.jsxs("span",{style:{fontSize:"13px",color:"var(--gray-500)"},children:["๋งˆ๊ฐ: ",s.deadline]})]})]}),e.jsx("div",{className:"divider divider-left",style:{margin:"24px 0"}}),e.jsx("h3",{style:{fontSize:"16px",fontWeight:"700",marginBottom:"12px"},children:"์—…๋ฌด ๋‚ด์šฉ"}),e.jsx("p",{style:{color:"var(--gray-700)",lineHeight:"1.8",marginBottom:"28px"},children:s.desc}),e.jsx("h3",{style:{fontSize:"16px",fontWeight:"700",marginBottom:"12px"},children:"๊ธฐ์ˆ  ์Šคํƒ"}),e.jsx("div",{style:{display:"flex",gap:"8px",flexWrap:"wrap",marginBottom:"28px"},children:s.stack.map((l,n)=>e.jsx("span",{style:{padding:"6px 14px",background:"var(--secondary)",color:"var(--accent)",borderRadius:"6px",fontSize:"13px",fontWeight:"600"},children:l},n))}),e.jsx("h3",{style:{fontSize:"16px",fontWeight:"700",marginBottom:"12px"},children:"์ง€์› ๋ฐฉ๋ฒ•"}),e.jsxs("p",{style:{color:"var(--gray-700)",lineHeight:"1.8"},children:["์ด๋ ฅ์„œ ๋ฐ ํฌํŠธํด๋ฆฌ์˜ค๋ฅผ ",e.jsx("strong",{children:"recruit@zioinfo.co.kr"})," ๋กœ ์ œ์ถœํ•˜์‹œ๊ฑฐ๋‚˜, ์•„๋ž˜ ์ง€์›ํ•˜๊ธฐ ๋ฒ„ํŠผ์„ ์ด์šฉํ•ด ์ฃผ์‹ญ์‹œ์˜ค."]}),e.jsx("a",{href:"/recruit/apply",className:"btn btn-primary btn-lg",style:{marginTop:"24px",display:"inline-flex"},children:"์ง€์›ํ•˜๊ธฐ โ†’"})]})]})})]})}return e.jsxs("main",{id:"main-content",className:"inner-page",children:[e.jsx(r,{title:"์ฑ„์šฉ๊ณต๊ณ "}),e.jsx("section",{className:"section",children:e.jsxs("div",{className:"container",children:[e.jsxs("div",{className:"section-header",children:[e.jsx("span",{className:"section-label",children:"Open Positions"}),e.jsx("h2",{className:"section-title",children:"ํ˜„์žฌ ์ฑ„์šฉ ์ค‘์ธ ํฌ์ง€์…˜"})]}),e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"16px"},children:o.map(s=>e.jsxs("div",{className:"card job-card",onClick:()=>i(s.id),children:[e.jsxs("div",{className:"job-info",children:[e.jsxs("div",{style:{display:"flex",gap:"8px",alignItems:"center",marginBottom:"8px"},children:[s.hot&&e.jsx("span",{className:"badge badge-new",children:"HOT"}),e.jsx("span",{className:"badge badge-primary",children:s.dept}),e.jsx("span",{className:"badge badge-accent",children:s.type})]}),e.jsx("h3",{className:"job-title",children:s.title}),e.jsx("p",{className:"job-desc",children:s.desc}),e.jsx("div",{className:"job-stack",children:s.stack.map((l,n)=>e.jsx("span",{className:"job-tech",children:l},n))})]}),e.jsxs("div",{className:"job-meta",children:[e.jsxs("div",{children:[e.jsx("span",{className:"job-meta-label",children:"๊ฒฝ๋ ฅ"}),e.jsx("span",{children:s.exp})]}),e.jsxs("div",{children:[e.jsx("span",{className:"job-meta-label",children:"๋งˆ๊ฐ"}),e.jsx("span",{style:{color:s.deadline==="์ƒ์‹œ"?"var(--accent)":"var(--gray-700)"},children:s.deadline})]}),e.jsx("button",{className:"btn btn-primary btn-sm",children:"์ƒ์„ธ๋ณด๊ธฐ"})]})]},s.id))})]})})]})}const g=[{cat:"๐Ÿ’ผ ๊ทผ๋ฌดํ™˜๊ฒฝ",items:[{icon:"๐Ÿ•˜",name:"์œ ์—ฐ๊ทผ๋ฌด์ œ",desc:"์ฝ”์–ดํƒ€์ž„(10์‹œ~16์‹œ) ์™ธ ์ž์œ ๋กœ์šด ์ถœํ‡ด๊ทผ ์‹œ๊ฐ„ ์„ ํƒ"},{icon:"๐Ÿ ",name:"์žฌํƒ๊ทผ๋ฌด",desc:"์ง๋ฌด์— ๋”ฐ๋ผ ์ฃผ 1~2ํšŒ ์žฌํƒ๊ทผ๋ฌด ์ง€์›"},{icon:"๐Ÿ’ป",name:"์žฅ๋น„ ์ง€์›",desc:"๋งฅ๋ถ ๋˜๋Š” ๊ณ ์„ฑ๋Šฅ ์œˆ๋„์šฐ ๋…ธํŠธ๋ถ ์„ ํƒ ์ง€๊ธ‰"},{icon:"๐ŸŽฏ",name:"๋ชฉํ‘œ ๊ด€๋ฆฌ(OKR)",desc:"๋ถ„๊ธฐ๋ณ„ OKR๋กœ ๋ช…ํ™•ํ•œ ๋ชฉํ‘œยท์„ฑ๊ณผ ๊ด€๋ฆฌ"}]},{cat:"๐Ÿ“š ์„ฑ์žฅ ์ง€์›",items:[{icon:"๐Ÿ“–",name:"๊ต์œก๋น„ ์ง€์›",desc:"์—ฐ 200๋งŒ์› ๊ต์œก๋น„ ์ง€์› (๋„์„œ, ๊ฐ•์˜, ์„ธ๋ฏธ๋‚˜)"},{icon:"๐Ÿ†",name:"์ž๊ฒฉ์ฆ ์ง€์›",desc:"์ •๋ณด์ฒ˜๋ฆฌ๊ธฐ์‚ฌ, PMP, AWS, Oracle ์ž๊ฒฉ์ฆ ์ทจ๋“ ์ง€์›"},{icon:"๐ŸŽ“",name:"์‚ฌ๋‚ด ๊ฐ•์˜",desc:"AIยทํด๋ผ์šฐ๋“œยท๋ณด์•ˆ ์›” 1ํšŒ ์‚ฌ๋‚ด ๊ธฐ์ˆ  ์„ธ๋ฏธ๋‚˜"},{icon:"โœˆ๏ธ",name:"์ปจํผ๋Ÿฐ์Šค",desc:"AWS re:Invent, Google I/O ๋“ฑ ๊ตญ๋‚ด์™ธ ์ปจํผ๋Ÿฐ์Šค ์ฐธ๊ฐ€ ์ง€์›"}]},{cat:"๐ŸŽ ๋ณต์ง€ ํ˜œํƒ",items:[{icon:"๐Ÿฅ",name:"๊ฑด๊ฐ•๊ฒ€์ง„",desc:"์—ฐ 1ํšŒ ์ข…ํ•ฉ๊ฑด๊ฐ•๊ฒ€์ง„ (๋ฐฐ์šฐ์ž ํฌํ•จ)"},{icon:"๐ŸŽ‚",name:"๊ฒฝ์กฐ์‚ฌ ์ง€์›",desc:"๊ฒฝ์กฐ๊ธˆยท๊ฒฝ์กฐํœด๊ฐ€ ์ œ๊ณต (๊ฒฐํ˜ผ, ์ถœ์‚ฐ, ์ƒ์กฐ)"},{icon:"๐Ÿฝ๏ธ",name:"์‹๋Œ€ ์ง€์›",desc:"์ ์‹ฌ ์‹๋Œ€ ์›” 15๋งŒ์› ์ง€์› (์‹๊ถŒ ๋˜๋Š” ์นด๋“œ)"},{icon:"๐ŸŽ‰",name:"๋ช…์ ˆ ์„ ๋ฌผ",desc:"์„คยท์ถ”์„ ๋ช…์ ˆ ์„ ๋ฌผ ๋ฐ ์ƒ์—ฌ๊ธˆ ์ง€๊ธ‰"}]}];function N(){return e.jsxs("main",{id:"main-content",className:"inner-page",children:[e.jsx(r,{title:"๋ณต๋ฆฌํ›„์ƒ"}),e.jsx("section",{className:"section",children:e.jsxs("div",{className:"container",children:[e.jsxs("div",{className:"section-header",children:[e.jsx("span",{className:"section-label",children:"Welfare"}),e.jsx("h2",{className:"section-title",children:"ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ํ™˜๊ฒฝ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค"}),e.jsx("p",{className:"section-desc",children:"๊ตฌ์„ฑ์›์ด ์ตœ๊ณ ์˜ ์—ญ๋Ÿ‰์„ ๋ฐœํœ˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹ค์–‘ํ•œ ์ง€์›์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค"})]}),g.map((a,i)=>e.jsxs("div",{style:{marginBottom:"56px"},children:[e.jsx("h3",{className:"welfare-cat",children:a.cat}),e.jsx("div",{className:"grid-4",children:a.items.map((s,l)=>e.jsxs("div",{className:"card welfare-card",children:[e.jsx("div",{className:"welfare-icon",children:s.icon}),e.jsx("h4",{className:"welfare-name",children:s.name}),e.jsx("p",{className:"welfare-desc",children:s.desc})]},l))})]},i)),e.jsxs("div",{className:"talent-wrap",children:[e.jsxs("div",{className:"section-header",children:[e.jsx("span",{className:"section-label",children:"Talent"}),e.jsx("h2",{className:"section-title",children:"์šฐ๋ฆฌ๊ฐ€ ์ฐพ๋Š” ์ธ์žฌ"})]}),e.jsx("div",{className:"grid-3",children:[{icon:"๐Ÿ”ฅ",title:"๋„์ „ํ•˜๋Š” ์ธ์žฌ",desc:"์ƒˆ๋กœ์šด ๊ธฐ์ˆ ๊ณผ ๋ฌธ์ œ์— ๋‘๋ ค์›€ ์—†์ด ๋„์ „ํ•˜๋Š” ๋ถ„"},{icon:"๐Ÿค",title:"ํ˜‘๋ ฅํ•˜๋Š” ์ธ์žฌ",desc:"ํŒ€๊ณผ ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋ฉฐ ์ง€์‹์„ ๋‚˜๋ˆ„๋Š” ๋ถ„"},{icon:"๐ŸŽฏ",title:"์ฑ…์ž„์ง€๋Š” ์ธ์žฌ",desc:"๋งก์€ ์—…๋ฌด์— ์˜ค๋„ˆ์‹ญ์„ ๊ฐ–๊ณ  ๋๊นŒ์ง€ ์™„์ˆ˜ํ•˜๋Š” ๋ถ„"}].map((a,i)=>e.jsxs("div",{className:"card",style:{padding:"36px 28px",textAlign:"center"},children:[e.jsx("div",{style:{fontSize:"48px",marginBottom:"16px"},children:a.icon}),e.jsx("h4",{style:{fontSize:"18px",fontWeight:"800",marginBottom:"12px"},children:a.title}),e.jsx("p",{style:{fontSize:"14px",color:"var(--gray-600)",lineHeight:"1.7"},children:a.desc})]},i))})]})]})})]})}function b(){const[a,i]=d.useState({name:"",email:"",phone:"",position:"",exp:"",portfolio:"",message:""}),[s,l]=d.useState(null),n=c=>i(m=>({...m,[c.target.name]:c.target.value})),x=c=>{c.preventDefault(),l("success")};return e.jsxs("main",{id:"main-content",className:"inner-page",children:[e.jsx(r,{title:"์ง€์›ํ•˜๊ธฐ"}),e.jsx("section",{className:"section",children:e.jsxs("div",{className:"container",style:{maxWidth:"720px"},children:[e.jsxs("div",{className:"section-header",children:[e.jsx("span",{className:"section-label",children:"Apply"}),e.jsx("h2",{className:"section-title",children:"์ž…์‚ฌ ์ง€์›์„œ"}),e.jsxs("p",{className:"section-desc",children:["์•„๋ž˜ ์–‘์‹์„ ์ž‘์„ฑํ•˜์‹œ๊ฑฐ๋‚˜ ",e.jsx("strong",{children:"recruit@zioinfo.co.kr"}),"๋กœ ์ด๋ ฅ์„œ๋ฅผ ๋ณด๋‚ด์ฃผ์„ธ์š”"]})]}),s==="success"?e.jsxs("div",{className:"apply-success",children:[e.jsx("div",{style:{fontSize:"64px",marginBottom:"20px"},children:"โœ…"}),e.jsx("h3",{children:"์ง€์›์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!"}),e.jsxs("p",{children:["๊ฒ€ํ†  ํ›„ ์˜์—…์ผ ๊ธฐ์ค€ 3~5์ผ ๋‚ด์— ์—ฐ๋ฝ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.",e.jsx("br",{}),"recruit@zioinfo.co.kr ๋กœ๋„ ์ด๋ ฅ์„œ๋ฅผ ์ถ”๊ฐ€ ์ œ์ถœํ•˜์‹œ๋ฉด ๋”์šฑ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค."]}),e.jsx("button",{className:"btn btn-outline",onClick:()=>l(null),style:{marginTop:"24px"},children:"๋‹ค์‹œ ์ง€์›ํ•˜๊ธฐ"})]}):e.jsxs("form",{className:"apply-form card",onSubmit:x,style:{padding:"40px"},children:[e.jsxs("div",{className:"form-row",children:[e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{children:["์„ฑ๋ช… ",e.jsx("span",{className:"required",children:"*"})]}),e.jsx("input",{name:"name",required:!0,value:a.name,onChange:n,placeholder:"ํ™๊ธธ๋™"})]}),e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{children:["์—ฐ๋ฝ์ฒ˜ ",e.jsx("span",{className:"required",children:"*"})]}),e.jsx("input",{name:"phone",required:!0,value:a.phone,onChange:n,placeholder:"010-0000-0000"})]})]}),e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{children:["์ด๋ฉ”์ผ ",e.jsx("span",{className:"required",children:"*"})]}),e.jsx("input",{name:"email",type:"email",required:!0,value:a.email,onChange:n,placeholder:"your@email.com"})]}),e.jsxs("div",{className:"form-row",children:[e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{children:["์ง€์› ํฌ์ง€์…˜ ",e.jsx("span",{className:"required",children:"*"})]}),e.jsxs("select",{name:"position",required:!0,value:a.position,onChange:n,children:[e.jsx("option",{value:"",children:"์„ ํƒํ•˜์„ธ์š”"}),o.map(c=>e.jsx("option",{value:c.title,children:c.title},c.id)),e.jsx("option",{value:"๊ธฐํƒ€",children:"๊ธฐํƒ€ (์ž์œ  ์ง€์›)"})]})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"๊ฒฝ๋ ฅ ์‚ฌํ•ญ"}),e.jsx("input",{name:"exp",value:a.exp,onChange:n,placeholder:"์˜ˆ: 5๋…„ (ํ˜„๋Œ€๋ฐฑํ™”์  โ†’ ์ง€์˜ค์ •๋ณด๊ธฐ์ˆ )"})]})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"ํฌํŠธํด๋ฆฌ์˜ค / GitHub URL"}),e.jsx("input",{name:"portfolio",value:a.portfolio,onChange:n,placeholder:"https://github.com/yourname"})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"์ž๊ธฐ์†Œ๊ฐœ ๋ฐ ์ง€์›๋™๊ธฐ"}),e.jsx("textarea",{name:"message",rows:6,value:a.message,onChange:n,placeholder:"๊ฐ„๋‹จํ•œ ์ž๊ธฐ์†Œ๊ฐœ์™€ ์ง€์›๋™๊ธฐ๋ฅผ ์ž‘์„ฑํ•ด ์ฃผ์„ธ์š”."})]}),e.jsx("button",{type:"submit",className:"btn btn-primary btn-lg",style:{width:"100%"},children:"์ง€์›์„œ ์ œ์ถœํ•˜๊ธฐ"})]})]})})]})}function y(){return e.jsxs(h,{children:[e.jsx(t,{path:"jobs",element:e.jsx(p,{})}),e.jsx(t,{path:"welfare",element:e.jsx(N,{})}),e.jsx(t,{path:"apply",element:e.jsx(b,{})}),e.jsx(t,{path:"*",element:e.jsx(p,{})})]})}export{y as default}; diff --git a/backend/src/main/resources/static/assets/SolutionPage-C7_Wcm8g.css b/backend/src/main/resources/static/assets/SolutionPage-C7_Wcm8g.css new file mode 100644 index 0000000..0b43b6c --- /dev/null +++ b/backend/src/main/resources/static/assets/SolutionPage-C7_Wcm8g.css @@ -0,0 +1 @@ +.sol-hero-grid{display:grid;grid-template-columns:1fr 1fr;gap:64px;align-items:center}.sol-title{font-size:clamp(26px,3.5vw,40px);font-weight:900;color:var(--gray-900);line-height:1.25;margin:12px 0 20px}.sol-title em{color:var(--primary);font-style:normal}.sol-desc{font-size:15px;color:var(--gray-600);line-height:1.85;margin-bottom:24px}.sol-features{display:flex;flex-direction:column;gap:10px}.sol-feature-item{display:flex;gap:10px;font-size:14px;color:var(--gray-700)}.sol-check{color:var(--accent);font-weight:700;flex-shrink:0}.sol-module-card{padding:32px 24px}.sol-module-icon{font-size:36px;margin-bottom:16px}.sol-module-card h3{font-size:16px;font-weight:700;margin-bottom:10px;color:var(--gray-900)}.sol-module-card p{font-size:13px;color:var(--gray-600);line-height:1.7}.sol-visual{display:flex;justify-content:center}.sol-screen{background:var(--secondary);border-radius:16px;padding:24px;width:100%;max-width:360px;box-shadow:var(--shadow-lg)}.sol-screen-header{display:flex;align-items:center;gap:8px;margin-bottom:20px;font-size:12px;color:#fff9;font-weight:600}.sol-screen-header span{width:10px;height:10px;border-radius:50%;background:var(--accent);flex-shrink:0}.sol-chart-bar-wrap{display:flex;gap:8px;height:120px;align-items:flex-end;margin-bottom:20px}.sol-chart-bar{flex:1;background:linear-gradient(to top,var(--primary),var(--accent));border-radius:4px 4px 0 0}.sol-stat-row{display:flex;gap:12px}.sol-stat{flex:1;background:#ffffff0f;border-radius:8px;padding:12px;text-align:center}.sol-stat strong{display:block;font-size:14px;color:#fff;font-weight:700}.sol-stat span{font-size:10px;color:#ffffff80;margin-top:4px;display:block}.crm-items{display:flex;flex-direction:column;gap:12px}.crm-item{display:flex;align-items:center;gap:12px;background:#ffffff0d;border-radius:8px;padding:10px 12px}.crm-avatar{width:32px;height:32px;border-radius:50%;background:var(--primary);display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;color:#fff;flex-shrink:0}.crm-info{flex:1}.crm-info strong{display:block;font-size:13px;color:#fff;font-weight:600}.crm-info span{font-size:11px;color:#ffffff80}.crm-status{font-size:11px;font-weight:700}.bi-kpis{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:20px}.bi-kpi{background:#ffffff0f;border-radius:8px;padding:12px}.bi-kpi-label{display:block;font-size:10px;color:#ffffff80;margin-bottom:4px}.bi-kpi-val{display:block;font-size:15px;color:#fff;font-weight:700}.bi-kpi-delta{font-size:11px;font-weight:600}.bi-bar-chart{display:flex;gap:12px;height:80px;align-items:flex-end}.bi-bar-group{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;height:100%}.bi-bar-pair{display:flex;gap:4px;width:100%;height:100%;align-items:flex-end}.bi-bar{flex:1;border-radius:3px 3px 0 0}.bi-bar.revenue{background:var(--accent)}.bi-bar.cost{background:#ef444499}.bi-bar-group span{font-size:10px;color:#ffffff80}@media (max-width: 768px){.sol-hero-grid{grid-template-columns:1fr}.sol-visual{order:-1}} diff --git a/backend/src/main/resources/static/assets/SolutionPage-Da0Vpoc-.js b/backend/src/main/resources/static/assets/SolutionPage-Da0Vpoc-.js new file mode 100644 index 0000000..cc70d8e --- /dev/null +++ b/backend/src/main/resources/static/assets/SolutionPage-Da0Vpoc-.js @@ -0,0 +1 @@ +import{j as s,b as r,a as i,L as c,N as d}from"./index-ChpGil2q.js";/* empty css */const t=[{path:"/solution/guardia",label:"GUARDiA ITSM",badge:"NEW"},{path:"/solution/erp",label:"ERP"},{path:"/solution/crm",label:"CRM"},{path:"/solution/bi",label:"BI"}];function n({title:l}){return s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"page-hero",children:s.jsxs("div",{className:"container",children:[s.jsx("span",{className:"section-label",children:"Solution"}),s.jsx("h1",{className:"page-hero-title",children:l})]})}),s.jsx("nav",{className:"sub-nav",children:s.jsx("div",{className:"container",children:t.map(e=>s.jsxs(d,{to:e.path,className:({isActive:a})=>"sub-nav-item"+(a?" active":""),children:[e.label,e.badge&&s.jsx("span",{className:"badge badge-new",style:{marginLeft:"6px",fontSize:"10px"},children:e.badge})]},e.path))})})]})}function o(){const l=[{icon:"๐Ÿ’ฐ",name:"์žฌ๋ฌดยทํšŒ๊ณ„",desc:"์ „ํ‘œ์ฒ˜๋ฆฌ, ๊ฒฐ์‚ฐ, ์„ธ๋ฌด์‹ ๊ณ , ์›๊ฐ€๊ณ„์‚ฐ ์ž๋™ํ™”"},{icon:"๐Ÿญ",name:"์ƒ์‚ฐ๊ด€๋ฆฌ",desc:"BOM ๊ด€๋ฆฌ, ์ƒ์‚ฐ๊ณ„ํš, ๊ณต์ •๊ด€๋ฆฌ, ํ’ˆ์งˆ๊ด€๋ฆฌ"},{icon:"๐Ÿ“ฆ",name:"๊ตฌ๋งคยท์žฌ๊ณ ",desc:"๋ฐœ์ฃผ, ์ž…์ถœ๊ณ , ์žฌ๊ณ  ํ˜„ํ™ฉ, ํ˜‘๋ ฅ์‚ฌ ํฌํ„ธ"},{icon:"๐Ÿ‘ฅ",name:"์ธ์‚ฌยท๊ธ‰์—ฌ",desc:"๊ทผํƒœ๊ด€๋ฆฌ, ๊ธ‰์—ฌ๊ณ„์‚ฐ, ์กฐ์ง๋„, ์ธ์‚ฌํ‰๊ฐ€"},{icon:"๐Ÿ›’",name:"์˜์—…ยท๋ฌผ๋ฅ˜",desc:"์ˆ˜์ฃผ๊ด€๋ฆฌ, ๋ฐฐ์†ก, ๋งค์ถœ ๋ถ„์„, ๊ณ ๊ฐ ๊ด€๋ฆฌ"},{icon:"๐Ÿ“Š",name:"๊ฒฝ์˜ ๋ถ„์„",desc:"KPI ๋Œ€์‹œ๋ณด๋“œ, ์˜ˆ์‚ฐ vs ์‹ค์ , ๊ฒฝ์˜ ๋ณด๊ณ ์„œ"}];return s.jsxs("main",{id:"main-content",className:"inner-page",children:[s.jsx(n,{title:"ERP ์†”๋ฃจ์…˜"}),s.jsx("section",{className:"section",children:s.jsxs("div",{className:"container",children:[s.jsxs("div",{className:"sol-hero-grid",children:[s.jsxs("div",{children:[s.jsx("span",{className:"section-label",children:"Enterprise Resource Planning"}),s.jsxs("h2",{className:"sol-title",children:["๊ณต๊ณตยท์ค‘๊ฒฌ๊ธฐ์—… ๋งž์ถคํ˜•",s.jsx("br",{}),s.jsx("em",{children:"ํ†ตํ•ฉ ERP ์†”๋ฃจ์…˜"})]}),s.jsx("p",{className:"sol-desc",children:"20๋…„ ์ด์ƒ ํ˜„๋Œ€๋ชจ๋น„์Šค, ํ•œํ™”๊ทธ๋ฃน, ์ด๋งˆํŠธ ๋“ฑ ๊ตญ๋‚ด ์ฃผ์š” ๊ธฐ์—…์˜ ํ•ต์‹ฌ ์—…๋ฌด ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•œ ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ, ๊ณ ๊ฐ์‚ฌ์˜ ์—…๋ฌด ํ”„๋กœ์„ธ์Šค์— ์ตœ์ ํ™”๋œ ๋งž์ถคํ˜• ERP๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค."}),s.jsx("div",{className:"sol-features",children:["๊ณต๊ณต๊ธฐ๊ด€ ํ‘œ์ค€ ํšŒ๊ณ„ ๊ธฐ์ค€ ์ ์šฉ","Oracle / Tibero DB ์ง€์›","๋ชจ๋ฐ”์ผ ๊ฒฐ์žฌยท๋ณด๊ณ  ์ง€์›","๊ธฐ์กด ๋ ˆ๊ฑฐ์‹œ ์‹œ์Šคํ…œ ์—ฐ๊ณ„"].map((e,a)=>s.jsxs("div",{className:"sol-feature-item",children:[s.jsx("span",{className:"sol-check",children:"โœ“"})," ",e]},a))}),s.jsxs("div",{style:{display:"flex",gap:"12px",marginTop:"32px",flexWrap:"wrap"},children:[s.jsx(c,{to:"/support/contact?type=๋ฐ๋ชจ ์‹ ์ฒญ",className:"btn btn-primary btn-lg",children:"๋ฌด๋ฃŒ ๋ฐ๋ชจ ์‹ ์ฒญ"}),s.jsx(c,{to:"/support/catalog",className:"btn btn-outline btn-lg",children:"์นดํƒˆ๋กœ๊ทธ ๋‹ค์šด๋กœ๋“œ"})]})]}),s.jsx("div",{className:"sol-visual erp-visual",children:s.jsxs("div",{className:"sol-screen",children:[s.jsxs("div",{className:"sol-screen-header",children:[s.jsx("span",{}),"์žฌ๋ฌด ๋Œ€์‹œ๋ณด๋“œ"]}),s.jsx("div",{className:"sol-chart-bar-wrap",children:[80,65,90,72,88,55,95].map((e,a)=>s.jsx("div",{className:"sol-chart-bar",style:{height:e+"%"}},a))}),s.jsxs("div",{className:"sol-stat-row",children:[s.jsxs("div",{className:"sol-stat",children:[s.jsx("strong",{children:"โ‚ฉ12.4์–ต"}),s.jsx("span",{children:"์ด๋ฒˆ๋‹ฌ ๋งค์ถœ"})]}),s.jsxs("div",{className:"sol-stat",children:[s.jsx("strong",{children:"98.2%"}),s.jsx("span",{children:"์˜ˆ์‚ฐ ์ง‘ํ–‰๋ฅ "})]}),s.jsxs("div",{className:"sol-stat",children:[s.jsx("strong",{children:"+18%"}),s.jsx("span",{children:"์ „์›” ๋Œ€๋น„"})]})]})]})})]}),s.jsxs("div",{style:{marginTop:"80px"},children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Modules"}),s.jsx("h2",{className:"section-title",children:"6๋Œ€ ํ•ต์‹ฌ ๋ชจ๋“ˆ"})]}),s.jsx("div",{className:"grid-3",children:l.map((e,a)=>s.jsxs("div",{className:"card sol-module-card",children:[s.jsx("div",{className:"sol-module-icon",children:e.icon}),s.jsx("h3",{children:e.name}),s.jsx("p",{children:e.desc})]},a))})]})]})})]})}function m(){const l=[{icon:"๐Ÿ“‡",name:"๊ณ ๊ฐ 360หš",desc:"๊ณ ๊ฐ ์ •๋ณด, ๊ตฌ๋งค์ด๋ ฅ, ์ƒ๋‹ด์ด๋ ฅ, ์„ ํ˜ธ๋„๋ฅผ ๋‹จ์ผ ๋ทฐ๋กœ ํ†ตํ•ฉ"},{icon:"๐Ÿ“ž",name:"๋ฉ€ํ‹ฐ์ฑ„๋„ ์ƒ๋‹ด",desc:"์ „ํ™”ยท์ด๋ฉ”์ผยท์ฑ„ํŒ…ยทSNS ํ†ตํ•ฉ ์ธ์ž…, ์ƒ๋‹ด ์ด๋ ฅ ์ž๋™ ๊ธฐ๋ก"},{icon:"๐ŸŽฏ",name:"์˜์—… ํŒŒ์ดํ”„๋ผ์ธ",desc:"๋ฆฌ๋“œ ๋ฐœ๊ตด๋ถ€ํ„ฐ ๊ณ„์•ฝ๊นŒ์ง€ ์ „ ๋‹จ๊ณ„ ์‹œ๊ฐํ™” ๊ด€๋ฆฌ"},{icon:"๐Ÿ“จ",name:"๋งˆ์ผ€ํŒ… ์ž๋™ํ™”",desc:"๊ณ ๊ฐ ์„ธ๊ทธ๋จผํŠธ๋ณ„ ์ž๋™ ์บ ํŽ˜์ธ, ์ด๋ฉ”์ผยทSMS ๋ฐœ์†ก"},{icon:"๐Ÿค–",name:"AI ์ƒ๋‹ด ์ถ”์ฒœ",desc:"Ollama LLM ๊ธฐ๋ฐ˜ ์ตœ์  ๋‹ต๋ณ€ ์ž๋™ ์ถ”์ฒœ ๋ฐ ์š”์•ฝ"},{icon:"๐Ÿ“ˆ",name:"์„ฑ๊ณผ ๋ถ„์„",desc:"์ƒ๋‹ด์‚ฌ๋ณ„ยท์ฑ„๋„๋ณ„ KPI, ๊ณ ๊ฐ ๋งŒ์กฑ๋„, ์ „ํ™˜์œจ ๋ฆฌํฌํŠธ"}];return s.jsxs("main",{id:"main-content",className:"inner-page",children:[s.jsx(n,{title:"CRM ์†”๋ฃจ์…˜"}),s.jsx("section",{className:"section",children:s.jsxs("div",{className:"container",children:[s.jsxs("div",{className:"sol-hero-grid",children:[s.jsxs("div",{children:[s.jsx("span",{className:"section-label",children:"Customer Relationship Management"}),s.jsxs("h2",{className:"sol-title",children:["AI ๊ธฐ๋ฐ˜",s.jsx("br",{}),s.jsx("em",{children:"๊ณ ๊ฐ ๊ด€๊ณ„ ๊ด€๋ฆฌ ํ”Œ๋žซํผ"})]}),s.jsx("p",{className:"sol-desc",children:"์‚ผ์„ฑ์ „์ž ์ฐจ์„ธ๋Œ€ CRM, LG U+ VAN ๊ณ ๋„ํ™”, ํ˜„๋Œ€์บํ”ผํƒˆ ์ฐจ์„ธ๋Œ€ ์‹œ์Šคํ…œ ๋“ฑ ๊ตญ๋‚ด ์ตœ๋Œ€ ๊ทœ๋ชจ CRM ํ”„๋กœ์ ํŠธ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•œ ์ „๋ฌธ ์—ญ๋Ÿ‰์œผ๋กœ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค. ์˜จํ”„๋ ˆ๋ฏธ์Šค AI(Ollama) ์—ฐ๋™์œผ๋กœ ๋ฐ์ดํ„ฐ ์™ธ๋ถ€ ์œ ์ถœ ์—†์ด ์ง€๋Šฅํ˜• ์ƒ๋‹ด์„ ์‹คํ˜„ํ•ฉ๋‹ˆ๋‹ค."}),s.jsx("div",{className:"sol-features",children:["์‚ผ์„ฑ์ „์žยทLGยทํ˜„๋Œ€ ๊ตฌ์ถ• ๋ ˆํผ๋Ÿฐ์Šค","์˜จํ”„๋ ˆ๋ฏธ์Šค AI ์ƒ๋‹ด ์ถ”์ฒœ","CTI ์—ฐ๋™ (์ฝœ์„ผํ„ฐ ์†”๋ฃจ์…˜)","๊ณต๊ณต๊ธฐ๊ด€ ๊ฐœ์ธ์ •๋ณด๋ณดํ˜ธ๋ฒ• ์ค€์ˆ˜"].map((e,a)=>s.jsxs("div",{className:"sol-feature-item",children:[s.jsx("span",{className:"sol-check",children:"โœ“"})," ",e]},a))}),s.jsxs("div",{style:{display:"flex",gap:"12px",marginTop:"32px",flexWrap:"wrap"},children:[s.jsx(c,{to:"/support/contact?type=๋ฐ๋ชจ ์‹ ์ฒญ",className:"btn btn-primary btn-lg",children:"๋ฐ๋ชจ ์‹ ์ฒญ"}),s.jsx(c,{to:"/support/catalog",className:"btn btn-outline btn-lg",children:"์นดํƒˆ๋กœ๊ทธ"})]})]}),s.jsx("div",{className:"sol-visual crm-visual",children:s.jsxs("div",{className:"sol-screen",children:[s.jsxs("div",{className:"sol-screen-header",children:[s.jsx("span",{}),"๊ณ ๊ฐ ์ƒ๋‹ด ํ˜„ํ™ฉ"]}),s.jsx("div",{className:"crm-items",children:[{name:"๊น€๋ฏผ์ค€",type:"์ œํ’ˆ๋ฌธ์˜",status:"์ฒ˜๋ฆฌ์ค‘",color:"#f59e0b"},{name:"์ด์„œ์—ฐ",type:"๊ธฐ์ˆ ์ง€์›",status:"์™„๋ฃŒ",color:"#10b981"},{name:"๋ฐ•์ง€ํ›„",type:"๋ถˆ๋งŒ์ ‘์ˆ˜",status:"๋Œ€๊ธฐ",color:"#ef4444"},{name:"์ตœ์ˆ˜์•„",type:"๋ฐ๋ชจ์‹ ์ฒญ",status:"์™„๋ฃŒ",color:"#10b981"}].map((e,a)=>s.jsxs("div",{className:"crm-item",children:[s.jsx("div",{className:"crm-avatar",children:e.name[0]}),s.jsxs("div",{className:"crm-info",children:[s.jsx("strong",{children:e.name}),s.jsx("span",{children:e.type})]}),s.jsx("span",{className:"crm-status",style:{color:e.color},children:e.status})]},a))})]})})]}),s.jsxs("div",{style:{marginTop:"80px"},children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Features"}),s.jsx("h2",{className:"section-title",children:"์ฃผ์š” ๊ธฐ๋Šฅ"})]}),s.jsx("div",{className:"grid-3",children:l.map((e,a)=>s.jsxs("div",{className:"card sol-module-card",children:[s.jsx("div",{className:"sol-module-icon",children:e.icon}),s.jsx("h3",{children:e.name}),s.jsx("p",{children:e.desc})]},a))})]})]})})]})}function h(){const l=[{icon:"๐Ÿ“Š",name:"๊ฒฝ์˜ ๋Œ€์‹œ๋ณด๋“œ",desc:"์‹ค์‹œ๊ฐ„ KPI ๋ชจ๋‹ˆํ„ฐ๋ง, ๋ถ€์„œ๋ณ„ ์„ฑ๊ณผ ์ง€ํ‘œ, ๊ฒฝ์˜์ง„ ์š”์•ฝ ๋ณด๊ณ "},{icon:"๐Ÿ“‰",name:"๋งค์ถœยท๋น„์šฉ ๋ถ„์„",desc:"๊ธฐ๊ฐ„๋ณ„ยท์ œํ’ˆ๋ณ„ยท์ฑ„๋„๋ณ„ ๋งค์ถœ ํŠธ๋ Œ๋“œ, ๋น„์šฉ ๊ตฌ์กฐ ๋ถ„์„"},{icon:"๐Ÿ—บ๏ธ",name:"์ง€์—ญ๋ณ„ ๋ถ„์„",desc:"์ง€๋„ ๊ธฐ๋ฐ˜ ์‹œ๊ฐํ™”, ๊ณต๊ณต๊ธฐ๊ด€ ์ง€์—ญ๋ณ„ ์„œ๋น„์Šค ํ˜„ํ™ฉ"},{icon:"๐Ÿ”ฎ",name:"AI ์˜ˆ์ธก ๋ถ„์„",desc:"๋จธ์‹ ๋Ÿฌ๋‹ ๊ธฐ๋ฐ˜ ์ˆ˜์š” ์˜ˆ์ธก, ์ด์ƒ ํŒจํ„ด ์ž๋™ ํƒ์ง€"},{icon:"๐Ÿ“‹",name:"์ž๋™ ๋ณด๊ณ ์„œ",desc:"์ผยท์ฃผยท์›” ๋ณด๊ณ ์„œ ์ž๋™ ์ƒ์„ฑ, ์ด๋ฉ”์ผยท๋ฉ”์‹ ์ € ๋ฐฐํฌ"},{icon:"๐Ÿ”—",name:"ETL ํŒŒ์ดํ”„๋ผ์ธ",desc:"Oracle, SAP, ๊ณต๊ณตDB ๋“ฑ ๋‹ค์–‘ํ•œ ์†Œ์Šค ๋ฐ์ดํ„ฐ ์—ฐ๊ณ„"}];return s.jsxs("main",{id:"main-content",className:"inner-page",children:[s.jsx(n,{title:"BI ์†”๋ฃจ์…˜"}),s.jsx("section",{className:"section",children:s.jsxs("div",{className:"container",children:[s.jsxs("div",{className:"sol-hero-grid",children:[s.jsxs("div",{children:[s.jsx("span",{className:"section-label",children:"Business Intelligence"}),s.jsxs("h2",{className:"sol-title",children:["๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜",s.jsx("br",{}),s.jsx("em",{children:"์˜์‚ฌ๊ฒฐ์ • ํ”Œ๋žซํผ"})]}),s.jsx("p",{className:"sol-desc",children:"OZ Report, MiPlatform, JasperReports ๋“ฑ ๋‹ค์–‘ํ•œ ๋ณด๊ณ  ๋„๊ตฌ์™€์˜ ์—ฐ๋™ ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ, ๊ณต๊ณต๊ธฐ๊ด€ยท์ค‘๊ฒฌ๊ธฐ์—… ๋งž์ถคํ˜• BI ํ”Œ๋žซํผ์„ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด ๋ ˆ๊ฑฐ์‹œ DB์—์„œ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•ด ๊ฒฝ์˜ ์ธ์‚ฌ์ดํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค."}),s.jsx("div",{className:"sol-features",children:["OZยทMiPlatformยทJasperReport ์—ฐ๋™","์‹ค์‹œ๊ฐ„ ๋Œ€์‹œ๋ณด๋“œ (WebSocket)","OracleยทTiberoยทPostgreSQL ์ง€์›","๊ณต๊ณต๊ธฐ๊ด€ ํ‘œ์ค€ ๋ณด๊ณ ์„œ ์–‘์‹"].map((e,a)=>s.jsxs("div",{className:"sol-feature-item",children:[s.jsx("span",{className:"sol-check",children:"โœ“"})," ",e]},a))}),s.jsxs("div",{style:{display:"flex",gap:"12px",marginTop:"32px",flexWrap:"wrap"},children:[s.jsx(c,{to:"/support/contact?type=๋ฐ๋ชจ ์‹ ์ฒญ",className:"btn btn-primary btn-lg",children:"๋ฐ๋ชจ ์‹ ์ฒญ"}),s.jsx(c,{to:"/support/catalog",className:"btn btn-outline btn-lg",children:"์นดํƒˆ๋กœ๊ทธ"})]})]}),s.jsx("div",{className:"sol-visual bi-visual",children:s.jsxs("div",{className:"sol-screen",children:[s.jsxs("div",{className:"sol-screen-header",children:[s.jsx("span",{}),"๊ฒฝ์˜ ๋Œ€์‹œ๋ณด๋“œ"]}),s.jsx("div",{className:"bi-kpis",children:[{label:"๋งค์ถœ",val:"โ‚ฉ48.2์–ต",up:!0,delta:"+12%"},{label:"๋น„์šฉ",val:"โ‚ฉ31.7์–ต",up:!1,delta:"-3%"},{label:"์ด์ต",val:"โ‚ฉ16.5์–ต",up:!0,delta:"+28%"},{label:"๊ณ ๊ฐ",val:"1,240๋ช…",up:!0,delta:"+8%"}].map((e,a)=>s.jsxs("div",{className:"bi-kpi",children:[s.jsx("span",{className:"bi-kpi-label",children:e.label}),s.jsx("strong",{className:"bi-kpi-val",children:e.val}),s.jsx("span",{className:"bi-kpi-delta",style:{color:e.up?"#10b981":"#ef4444"},children:e.delta})]},a))}),s.jsx("div",{className:"bi-bar-chart",children:["1Q","2Q","3Q","4Q"].map((e,a)=>s.jsxs("div",{className:"bi-bar-group",children:[s.jsxs("div",{className:"bi-bar-pair",children:[s.jsx("div",{className:"bi-bar revenue",style:{height:[60,75,85,100][a]+"%"}}),s.jsx("div",{className:"bi-bar cost",style:{height:[70,65,60,55][a]+"%"}})]}),s.jsx("span",{children:e})]},a))})]})})]}),s.jsxs("div",{style:{marginTop:"80px"},children:[s.jsxs("div",{className:"section-header",children:[s.jsx("span",{className:"section-label",children:"Features"}),s.jsx("h2",{className:"section-title",children:"์ฃผ์š” ๊ธฐ๋Šฅ"})]}),s.jsx("div",{className:"grid-3",children:l.map((e,a)=>s.jsxs("div",{className:"card sol-module-card",children:[s.jsx("div",{className:"sol-module-icon",children:e.icon}),s.jsx("h3",{children:e.name}),s.jsx("p",{children:e.desc})]},a))})]})]})})]})}function p(){return s.jsxs(r,{children:[s.jsx(i,{path:"erp",element:s.jsx(o,{})}),s.jsx(i,{path:"crm",element:s.jsx(m,{})}),s.jsx(i,{path:"bi",element:s.jsx(h,{})})]})}export{p as default}; diff --git a/backend/src/main/resources/static/assets/Support-C5QVP1gW.js b/backend/src/main/resources/static/assets/Support-C5QVP1gW.js new file mode 100644 index 0000000..4b5efb0 --- /dev/null +++ b/backend/src/main/resources/static/assets/Support-C5QVP1gW.js @@ -0,0 +1 @@ +import{j as e,b as j,a as n,r as p,N}from"./index-ChpGil2q.js";/* empty css */const A=[{path:"/support/notice",label:"๊ณต์ง€์‚ฌํ•ญ"},{path:"/support/faq",label:"FAQ"},{path:"/support/catalog",label:"์นดํƒˆ๋กœ๊ทธ"},{path:"/support/contact",label:"๋ฌธ์˜ํ•˜๊ธฐ"}];function h({title:s}){return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"page-hero",children:e.jsxs("div",{className:"container",children:[e.jsx("span",{className:"section-label",children:"Support"}),e.jsx("h1",{className:"page-hero-title",children:s})]})}),e.jsx("nav",{className:"sub-nav",children:e.jsx("div",{className:"container",children:A.map(t=>e.jsx(N,{to:t.path,className:({isActive:a})=>"sub-nav-item"+(a?" active":""),children:t.label},t.path))})})]})}const u=[{id:1,cat:"๊ณต์ง€",title:"GUARDiA ITSM v2.0 ์ •์‹ ์ถœ์‹œ ์•ˆ๋‚ด",date:"2026.05.15",hot:!0},{id:2,cat:"๊ณต์ง€",title:"2026๋…„ ์ƒ๋ฐ˜๊ธฐ ์œ ์ง€๋ณด์ˆ˜ ์ ๊ฒ€ ์ผ์ • ์•ˆ๋‚ด (6์›” 1์ผ~2์ผ)",date:"2026.05.10",hot:!1},{id:3,cat:"๋ณด์•ˆ",title:"Apache Log4j ์ทจ์•ฝ์  ๊ธด๊ธ‰ ํŒจ์น˜ ์•ˆ๋‚ด",date:"2026.04.28",hot:!1},{id:4,cat:"๊ณต์ง€",title:"๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจ ๊ฐœ์ • ์•ˆ๋‚ด (2026๋…„ 4์›”)",date:"2026.04.01",hot:!1},{id:5,cat:"์ด๋ฒคํŠธ",title:"2026 ๊ณต๊ณต๊ธฐ๊ด€ ๋””์ง€ํ„ธ์ „ํ™˜ ์„ธ๋ฏธ๋‚˜ ์ฐธ๊ฐ€ ์•ˆ๋‚ด (5์›” 20์ผ)",date:"2026.03.25",hot:!1},{id:6,cat:"๊ณต์ง€",title:"GUARDiA ITSM GS์ธ์ฆ 1๋“ฑ๊ธ‰ ์‹ ์ฒญ ์™„๋ฃŒ ์•ˆ๋‚ด",date:"2026.03.10",hot:!1},{id:7,cat:"๊ณต์ง€",title:"์‹ ๊ทœ ํŒŒํŠธ๋„ˆ์‚ฌ ํ˜‘์•ฝ ์ฒด๊ฒฐ โ€” Tibero ๊ณต์‹ ํŒŒํŠธ๋„ˆ ๋“ฑ๋ก",date:"2026.02.20",hot:!1},{id:8,cat:"๋ณด์•ˆ",title:"2026๋…„ ์ •๋ณด๋ณด์•ˆ ๊ต์œก ์‹ค์‹œ ์•ˆ๋‚ด (์ž„์ง์› ํ•„๋…)",date:"2026.01.15",hot:!1},{id:9,cat:"๊ณต์ง€",title:"2025๋…„ ์‚ฌ์—…์„ฑ๊ณผ ๋ฐ 2026๋…„ ์‚ฌ์—…๊ณ„ํš ๋ฐœํ‘œ",date:"2026.01.02",hot:!1},{id:10,cat:"๊ณต์ง€",title:"์—ฐ๋ง์—ฐ์‹œ ๊ณ ๊ฐ์ง€์›ํŒ€ ์šด์˜์‹œ๊ฐ„ ์•ˆ๋‚ด (12/24~1/3)",date:"2025.12.20",hot:!1}],d={๊ณต์ง€:"var(--primary)",๋ณด์•ˆ:"var(--danger)",์ด๋ฒคํŠธ:"var(--accent)"};function x(){const[s,t]=p.useState(null);return e.jsxs("main",{id:"main-content",className:"inner-page",children:[e.jsx(h,{title:"๊ณต์ง€์‚ฌํ•ญ"}),e.jsx("section",{className:"section",children:e.jsx("div",{className:"container",style:{maxWidth:"860px"},children:s?e.jsxs("div",{className:"notice-detail",children:[e.jsx("button",{className:"notice-back",onClick:()=>t(null),children:"โ† ๋ชฉ๋ก์œผ๋กœ"}),e.jsxs("div",{className:"notice-detail-header",children:[e.jsx("span",{className:"notice-cat",style:{background:d[s.cat]+"18",color:d[s.cat]},children:s.cat}),e.jsx("h2",{children:s.title}),e.jsx("p",{className:"notice-date",children:s.date})]}),e.jsxs("div",{className:"notice-body",children:[e.jsx("p",{children:"์•ˆ๋…•ํ•˜์„ธ์š”, (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค."}),e.jsxs("p",{children:["๋ณธ ๊ณต์ง€๋Š” ",e.jsx("strong",{children:s.title}),"์— ๊ด€ํ•œ ์•ˆ๋‚ด์ž…๋‹ˆ๋‹ค."]}),e.jsx("p",{children:"์ž์„ธํ•œ ์‚ฌํ•ญ์€ ๊ณ ๊ฐ์ง€์›ํŒ€(02-784-9271)์œผ๋กœ ๋ฌธ์˜ํ•ด ์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค."}),e.jsx("p",{children:"๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค."})]})]}):e.jsxs("div",{className:"notice-list",children:[e.jsxs("div",{className:"notice-header-row",children:[e.jsx("span",{children:"๊ตฌ๋ถ„"}),e.jsx("span",{children:"์ œ๋ชฉ"}),e.jsx("span",{children:"๋“ฑ๋ก์ผ"})]}),u.map(a=>e.jsxs("div",{className:"notice-row",onClick:()=>t(a),children:[e.jsx("span",{className:"notice-cat",style:{background:d[a.cat]+"18",color:d[a.cat]},children:a.cat}),e.jsxs("span",{className:"notice-title-text",children:[a.hot&&e.jsx("span",{className:"notice-hot",children:"HOT"}),a.title]}),e.jsx("span",{className:"notice-date",children:a.date})]},a.id))]})})})]})}const v=[{cat:"GUARDiA ITSM",items:[{q:"GUARDiA ITSM์€ ์–ด๋–ค ์ œํ’ˆ์ธ๊ฐ€์š”?",a:"GUARDiA ITSM์€ ๋ฉ”์‹ ์ € ํ•œ ์ค„ ๋ช…๋ น์œผ๋กœ 1,000๊ฐœ ์ด์ƒ ๊ณต๊ณต๊ธฐ๊ด€์˜ ๋ ˆ๊ฑฐ์‹œ IT ์ธํ”„๋ผ๋ฅผ ์ž๋™ ์šด์˜ํ•˜๋Š” AI ๊ธฐ๋ฐ˜ ChatOps ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. ๋Œ€์ƒ ์„œ๋ฒ„์— ๋ณ„๋„ ์†Œํ”„ํŠธ์›จ์–ด ์„ค์น˜ ์—†์ด ํ‘œ์ค€ SSH/SFTP ํ”„๋กœํ† ์ฝœ๋งŒ์œผ๋กœ ๋ฐฐํฌยท์šด์˜ยท๋ชจ๋‹ˆํ„ฐ๋ง์„ ์ž๋™ํ™”ํ•ฉ๋‹ˆ๋‹ค."},{q:"์„œ๋ฒ„์— ์—์ด์ „ํŠธ๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•˜๋‚˜์š”?",a:"์•„๋‹ˆ์š”. GUARDiA ITSM์€ ์—์ด์ „ํŠธ๋ฆฌ์Šค(Agentless) ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€์ƒ ์„œ๋ฒ„์— ์–ด๋– ํ•œ ์†Œํ”„ํŠธ์›จ์–ด๋„ ์„ค์น˜ํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฉฐ, ํ‘œ์ค€ SSH(22๋ฒˆ ํฌํŠธ)๋งŒ ์—ด๋ ค ์žˆ์œผ๋ฉด ์ฆ‰์‹œ ์—ฐ๋™ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."},{q:"ํด๋ผ์šฐ๋“œ ์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‚˜์š”?",a:"์˜ˆ. GUARDiA ITSM์€ ์™„์ „ํ•œ ์˜จํ”„๋ ˆ๋ฏธ์Šค(On-premise) ์†”๋ฃจ์…˜์œผ๋กœ, ์™ธ๋ถ€ ํด๋ผ์šฐ๋“œ๋‚˜ ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ ์—†์ด ํ์‡„๋ง ํ™˜๊ฒฝ์—์„œ๋„ 100% ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. AI ์—”์ง„(Ollama)๋„ ๋‚ด๋ถ€ ์„œ๋ฒ„์—์„œ ๊ตฌ๋™๋ฉ๋‹ˆ๋‹ค."},{q:"์ง€์›๋˜๋Š” ์šด์˜์ฒด์ œ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?",a:"GUARDiA ITSM ์„œ๋ฒ„: Ubuntu 20.04+, CentOS 7+, RHEL 8+, Windows Server 2019+๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ ๋Œ€์ƒ ์„œ๋ฒ„: SSH๊ฐ€ ์ง€์›๋˜๋Š” ๋ชจ๋“  Linux/Unix/Windows Server ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."}]},{cat:"๋„์ž…ยท๊ณ„์•ฝ",items:[{q:"๋„์ž… ๋น„์šฉ์€ ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”?",a:"๊ธฐ๊ด€ ๊ทœ๋ชจ์™€ ๊ด€๋ฆฌ ์„œ๋ฒ„ ์ˆ˜์— ๋”ฐ๋ผ ๋งž์ถค ๊ฒฌ์ ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. 7์ผ ๋ฌด๋ฃŒ ์ฒดํ—˜ํŒ์„ ๋จผ์ € ์‹ ์ฒญํ•˜์‹  ํ›„ ๋ฌธ์˜ ์ฃผ์‹œ๋ฉด ์ƒ์„ธํ•œ ๊ฒฌ์ ์„ ์•ˆ๋‚ดํ•ด ๋“œ๋ฆฝ๋‹ˆ๋‹ค."},{q:"์ฒดํ—˜ํŒ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‚˜์š”?",a:'์˜ˆ. 7์ผ ๋ฌด๋ฃŒ ์ฒดํ—˜ํŒ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์˜ํ•˜๊ธฐ ๋˜๋Š” GUARDiA ํŽ˜์ด์ง€์˜ "๋ฌด๋ฃŒ ๋ฐ๋ชจ ์‹ ์ฒญ" ๋ฒ„ํŠผ์„ ํ†ตํ•ด ์‹ ์ฒญํ•˜์‹œ๋ฉด ์˜์—…์ผ ๊ธฐ์ค€ 1์ผ ์ด๋‚ด์— ์•ˆ๋‚ด ๋“œ๋ฆฝ๋‹ˆ๋‹ค.'},{q:"๊ณต๊ณต๊ธฐ๊ด€ ๋‚˜๋ผ์žฅํ„ฐ ์กฐ๋‹ฌ ๊ตฌ๋งค๊ฐ€ ๊ฐ€๋Šฅํ•œ๊ฐ€์š”?",a:"์˜ˆ. GUARDiA ITSM์€ ์กฐ๋‹ฌ์ฒญ ๋‚˜๋ผ์žฅํ„ฐ ๋“ฑ๋ก์„ ์ค€๋น„ ์ค‘์ด๋ฉฐ, ๊ณต๊ณต๊ธฐ๊ด€ ์ž…์ฐฐ์„ ํ†ตํ•œ ๊ตฌ๋งค๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ์‚ฌํ•ญ์€ ์˜์—…ํŒ€(02-784-9271)์— ๋ฌธ์˜ํ•ด ์ฃผ์‹ญ์‹œ์˜ค."}]},{cat:"๊ธฐ์ˆ  ์ง€์›",items:[{q:"๊ธฐ์ˆ  ์ง€์›์€ ์–ด๋–ป๊ฒŒ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‚˜์š”?",a:"์ด๋ฉ”์ผ(support@zioinfo.co.kr), ์ „ํ™”(02-784-9271), GUARDiA ITSM ๋‚ด ์ฑ—๋ด‡์„ ํ†ตํ•ด ๊ธฐ์ˆ  ์ง€์›์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์šด์˜ ์ค‘ ๊ธด๊ธ‰ ์žฅ์• ๋Š” 24์‹œ๊ฐ„ ์˜จ์ฝœ ์ง€์›์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."},{q:"์—…๊ทธ๋ ˆ์ด๋“œ๋Š” ์–ด๋–ป๊ฒŒ ์ง„ํ–‰๋˜๋‚˜์š”?",a:"์ •๊ธฐ ์—…๋ฐ์ดํŠธ๋Š” ์—ฐ 2~4ํšŒ ์ œ๊ณต๋˜๋ฉฐ, ๋ณด์•ˆ ํŒจ์น˜๋Š” ์ฆ‰์‹œ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์—…๊ทธ๋ ˆ์ด๋“œ๋Š” GUARDiA ๋‚ด ์ž๋™ ๋ฐฐํฌ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ๋‹ค์šดํƒ€์ž„ ์—†์ด ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."}]}];function g(){const[s,t]=p.useState({}),a=(i,c)=>t(l=>({...l,[`${i}-${c}`]:!l[`${i}-${c}`]}));return e.jsxs("main",{id:"main-content",className:"inner-page",children:[e.jsx(h,{title:"์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ"}),e.jsx("section",{className:"section",children:e.jsxs("div",{className:"container",style:{maxWidth:"860px"},children:[e.jsxs("div",{className:"section-header",children:[e.jsx("span",{className:"section-label",children:"FAQ"}),e.jsx("h2",{className:"section-title",children:"์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ"})]}),v.map((i,c)=>e.jsxs("div",{className:"faq-cat-wrap",children:[e.jsx("h3",{className:"faq-cat-title",children:i.cat}),i.items.map((l,r)=>{const m=`${c}-${r}`,o=s[m];return e.jsxs("div",{className:`faq-item ${o?"open":""}`,children:[e.jsxs("button",{className:"faq-q",onClick:()=>a(c,r),children:[e.jsx("span",{className:"faq-icon",children:o?"โˆ’":"+"}),l.q]}),o&&e.jsx("div",{className:"faq-a",children:l.a})]},r)})]},c)),e.jsxs("div",{className:"faq-more",children:[e.jsx("p",{children:"์›ํ•˜์‹œ๋Š” ๋‹ต๋ณ€์„ ์ฐพ์ง€ ๋ชปํ•˜์…จ๋‚˜์š”?"}),e.jsx("a",{href:"/support/contact",className:"btn btn-primary",children:"1:1 ๋ฌธ์˜ํ•˜๊ธฐ"})]})]})})]})}const S=[{title:"GUARDiA ITSM v2.0 ์ œํ’ˆ ์นดํƒˆ๋กœ๊ทธ",desc:"GUARDiA ITSM ์ „์ฒด ๊ธฐ๋Šฅ ๋ฐ ๋„์ž… ๊ฐ€์ด๋“œ",pages:"24p",size:"4.2MB",date:"2026.05",icon:"โšก",color:"var(--primary)"},{title:"GUARDiA ITSM ๊ธฐ์ˆ  ๋ช…์„ธ์„œ",desc:"API ๋ช…์„ธ, ์•„ํ‚คํ…์ฒ˜, ๋ณด์•ˆ ์„ค๊ณ„ ๋ฌธ์„œ",pages:"48p",size:"8.1MB",date:"2026.05",icon:"๐Ÿ“",color:"#7c3aed"},{title:"ERP ์†”๋ฃจ์…˜ ์นดํƒˆ๋กœ๊ทธ",desc:"๊ณต๊ณตยท์ค‘๊ฒฌ๊ธฐ์—… ๋งž์ถคํ˜• ERP ๋ชจ๋“ˆ ์†Œ๊ฐœ",pages:"16p",size:"3.5MB",date:"2026.03",icon:"๐Ÿ’ฐ",color:"#059669"},{title:"CRM ์†”๋ฃจ์…˜ ์นดํƒˆ๋กœ๊ทธ",desc:"AI ๊ธฐ๋ฐ˜ ๊ณ ๊ฐ๊ด€๋ฆฌ ํ”Œ๋žซํผ ์†Œ๊ฐœ",pages:"12p",size:"2.8MB",date:"2026.03",icon:"๐Ÿ“ž",color:"#d97706"},{title:"BI ์†”๋ฃจ์…˜ ์นดํƒˆ๋กœ๊ทธ",desc:"๊ฒฝ์˜ ๋ถ„์„ ๋ฐ ๋Œ€์‹œ๋ณด๋“œ ํ”Œ๋žซํผ ์†Œ๊ฐœ",pages:"12p",size:"2.4MB",date:"2026.02",icon:"๐Ÿ“Š",color:"#0891b2"},{title:"์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ํšŒ์‚ฌ ์†Œ๊ฐœ์„œ",desc:"ํšŒ์‚ฌ ์—ฐํ˜, ์‚ฌ์—… ์˜์—ญ, ์ฃผ์š” ๋ ˆํผ๋Ÿฐ์Šค",pages:"20p",size:"5.6MB",date:"2026.01",icon:"๐Ÿข",color:"var(--secondary)"}];function b(){return e.jsxs("main",{id:"main-content",className:"inner-page",children:[e.jsx(h,{title:"์นดํƒˆ๋กœ๊ทธ"}),e.jsx("section",{className:"section",children:e.jsxs("div",{className:"container",children:[e.jsxs("div",{className:"section-header",children:[e.jsx("span",{className:"section-label",children:"Catalog"}),e.jsx("h2",{className:"section-title",children:"์ž๋ฃŒ์‹ค"}),e.jsx("p",{className:"section-desc",children:"์ œํ’ˆ ์นดํƒˆ๋กœ๊ทธ ๋ฐ ๊ธฐ์ˆ  ๋ฌธ์„œ๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜์„ธ์š”"})]}),e.jsx("div",{className:"grid-3",children:S.map((s,t)=>e.jsxs("div",{className:"card catalog-card",children:[e.jsx("div",{className:"catalog-icon-wrap",style:{background:s.color+"18"},children:e.jsx("span",{className:"catalog-icon",style:{color:s.color},children:s.icon})}),e.jsxs("div",{className:"catalog-info",children:[e.jsx("h3",{className:"catalog-title",children:s.title}),e.jsx("p",{className:"catalog-desc",children:s.desc}),e.jsxs("div",{className:"catalog-meta",children:[e.jsx("span",{children:s.pages}),e.jsx("span",{children:s.size}),e.jsx("span",{children:s.date})]})]}),e.jsx("button",{className:"catalog-btn",onClick:()=>alert("๋ฌธ์˜ํ•˜๊ธฐ ํŽ˜์ด์ง€์—์„œ ์นดํƒˆ๋กœ๊ทธ๋ฅผ ์š”์ฒญํ•ด์ฃผ์„ธ์š”."),children:"๐Ÿ“ฅ ๋‹ค์šด๋กœ๋“œ"})]},t))}),e.jsxs("div",{className:"catalog-request",children:[e.jsx("h3",{children:"๋‹ค๋ฅธ ์ž๋ฃŒ๊ฐ€ ํ•„์š”ํ•˜์‹ ๊ฐ€์š”?"}),e.jsx("p",{children:"NDA ์ž๋ฃŒ, ๊ธฐ์ˆ  ์ œ์•ˆ์„œ, ๊ฐ€๊ฒฉํ‘œ ๋“ฑ ๋ณ„๋„ ๋ฌธ์„œ๋Š” ์˜์—…ํŒ€์— ์š”์ฒญํ•ด ์ฃผ์‹ญ์‹œ์˜ค."}),e.jsx("a",{href:"/support/contact",className:"btn btn-outline btn-lg",children:"์ž๋ฃŒ ์š”์ฒญํ•˜๊ธฐ"})]})]})})]})}function I(){return e.jsxs(j,{children:[e.jsx(n,{path:"notice",element:e.jsx(x,{})}),e.jsx(n,{path:"faq",element:e.jsx(g,{})}),e.jsx(n,{path:"catalog",element:e.jsx(b,{})}),e.jsx(n,{path:"*",element:e.jsx(x,{})})]})}export{I as default}; diff --git a/backend/src/main/resources/static/assets/Support-DvGESosS.css b/backend/src/main/resources/static/assets/Support-DvGESosS.css new file mode 100644 index 0000000..59db598 --- /dev/null +++ b/backend/src/main/resources/static/assets/Support-DvGESosS.css @@ -0,0 +1 @@ +.notice-header-row{display:grid;grid-template-columns:80px 1fr 100px;gap:16px;padding:12px 16px;background:var(--gray-50);border-radius:8px 8px 0 0;font-size:12px;font-weight:700;color:var(--gray-500);border:1px solid var(--gray-200);border-bottom:none}.notice-row{display:grid;grid-template-columns:80px 1fr 100px;gap:16px;padding:14px 16px;border:1px solid var(--gray-200);border-top:none;cursor:pointer;align-items:center;transition:background var(--fast)}.notice-row:last-child{border-radius:0 0 8px 8px}.notice-row:hover{background:var(--gray-50)}.notice-cat{display:inline-block;padding:3px 10px;border-radius:12px;font-size:11px;font-weight:700;text-align:center}.notice-title-text{font-size:14px;color:var(--gray-800);display:flex;align-items:center;gap:8px}.notice-hot{background:var(--danger);color:#fff;font-size:10px;padding:2px 6px;border-radius:4px;font-weight:700;flex-shrink:0}.notice-date{font-size:12px;color:var(--gray-500)}.notice-detail{max-width:760px}.notice-back{font-size:14px;color:var(--primary);margin-bottom:24px;display:inline-flex;align-items:center;gap:4px;cursor:pointer;background:none;border:none}.notice-detail-header{border-bottom:2px solid var(--gray-200);padding-bottom:20px;margin-bottom:32px}.notice-detail-header h2{font-size:22px;font-weight:800;margin:12px 0 8px}.notice-body{display:flex;flex-direction:column;gap:16px;font-size:15px;line-height:1.85;color:var(--gray-700)}.faq-cat-wrap{margin-bottom:40px}.faq-cat-title{font-size:16px;font-weight:800;color:var(--primary);margin-bottom:12px;padding-bottom:8px;border-bottom:2px solid var(--primary-light)}.faq-item{border:1px solid var(--gray-200);border-radius:8px;margin-bottom:8px;overflow:hidden;transition:box-shadow var(--fast)}.faq-item.open{box-shadow:var(--shadow);border-color:var(--primary-light)}.faq-q{width:100%;display:flex;align-items:center;gap:14px;padding:16px 20px;font-size:15px;font-weight:600;color:var(--gray-800);text-align:left;background:none;border:none;cursor:pointer;transition:background var(--fast)}.faq-q:hover{background:var(--gray-50)}.faq-icon{width:24px;height:24px;border-radius:50%;background:var(--primary);color:#fff;display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0}.faq-a{padding:0 20px 20px 58px;font-size:14px;color:var(--gray-600);line-height:1.8}.faq-more{text-align:center;padding:48px;background:var(--gray-50);border-radius:12px;margin-top:32px}.faq-more p{color:var(--gray-600);margin-bottom:20px;font-size:16px}.catalog-card{padding:0;display:flex;flex-direction:column}.catalog-icon-wrap{padding:32px;display:flex;align-items:center;justify-content:center}.catalog-icon{font-size:48px}.catalog-info{padding:0 24px 16px;flex:1}.catalog-title{font-size:15px;font-weight:700;margin-bottom:8px;color:var(--gray-900)}.catalog-desc{font-size:13px;color:var(--gray-600);margin-bottom:12px;line-height:1.6}.catalog-meta{display:flex;gap:12px;font-size:12px;color:var(--gray-400)}.catalog-btn{margin:0 16px 20px;padding:12px;border-radius:8px;background:var(--gray-50);border:1px solid var(--gray-200);font-size:14px;font-weight:600;color:var(--primary);cursor:pointer;transition:all var(--fast);text-align:center}.catalog-btn:hover{background:var(--primary);color:#fff;border-color:var(--primary)}.catalog-request{text-align:center;padding:56px;background:var(--gray-50);border-radius:16px;margin-top:48px}.catalog-request h3{font-size:22px;font-weight:800;margin-bottom:12px}.catalog-request p{color:var(--gray-600);margin-bottom:24px} diff --git a/backend/src/main/resources/static/assets/admin-BHL-7hu0.css b/backend/src/main/resources/static/assets/admin-BHL-7hu0.css new file mode 100644 index 0000000..2e0e621 --- /dev/null +++ b/backend/src/main/resources/static/assets/admin-BHL-7hu0.css @@ -0,0 +1 @@ +:root{--admin-sidebar-w: 220px;--admin-bg: #f0f2f5;--admin-sidebar-bg: #1a1d2e;--admin-sidebar-hover: #2a2d3e;--admin-accent: #4f6ef7;--admin-accent-hover: #3a5be0;--admin-text: #1e293b;--admin-muted: #64748b;--admin-border: #e2e8f0;--admin-card: #ffffff;--admin-danger: #ef4444;--admin-success: #22c55e;--admin-warning: #f59e0b}.admin-wrap{display:flex;min-height:100vh;background:var(--admin-bg);font-family:Pretendard,-apple-system,sans-serif}.admin-sidebar{width:var(--admin-sidebar-w);background:var(--admin-sidebar-bg);display:flex;flex-direction:column;position:fixed;top:0;left:0;height:100vh;z-index:100;transition:transform .25s}.admin-sidebar-logo{padding:20px 20px 16px;border-bottom:1px solid rgba(255,255,255,.08)}.admin-sidebar-logo h2{color:#fff;font-size:15px;font-weight:700;margin:0}.admin-sidebar-logo span{color:#7c85a8;font-size:11px}.admin-nav{flex:1;overflow-y:auto;padding:12px 0}.admin-nav-section{padding:12px 16px 4px;color:#7c85a8;font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase}.admin-nav a{display:flex;align-items:center;gap:10px;padding:9px 20px;color:#b0b7cc;text-decoration:none;font-size:13.5px;border-radius:0;transition:all .15s}.admin-nav a:hover{background:var(--admin-sidebar-hover);color:#fff}.admin-nav a.active{background:var(--admin-accent);color:#fff}.admin-nav a .nav-icon{width:16px;text-align:center;flex-shrink:0}.admin-nav-badge{background:var(--admin-danger);color:#fff;font-size:10px;padding:1px 6px;border-radius:10px;margin-left:auto}.admin-sidebar-footer{padding:16px 20px;border-top:1px solid rgba(255,255,255,.08)}.admin-sidebar-footer button{width:100%;background:transparent;border:1px solid rgba(255,255,255,.15);color:#b0b7cc;padding:8px 12px;border-radius:6px;font-size:13px;cursor:pointer;transition:all .15s}.admin-sidebar-footer button:hover{background:#ffffff14;color:#fff}.admin-main{margin-left:var(--admin-sidebar-w);flex:1;display:flex;flex-direction:column;min-height:100vh}.admin-topbar{background:var(--admin-card);border-bottom:1px solid var(--admin-border);padding:0 28px;height:56px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:50}.admin-topbar h1{font-size:16px;font-weight:600;color:var(--admin-text);margin:0}.admin-topbar-right{display:flex;align-items:center;gap:12px}.admin-user-badge{background:#e8ecff;color:var(--admin-accent);padding:4px 12px;border-radius:20px;font-size:12px;font-weight:600}.admin-content{padding:28px;flex:1}.admin-card{background:var(--admin-card);border-radius:10px;border:1px solid var(--admin-border);padding:20px}.admin-card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}.admin-card-header h3{font-size:14px;font-weight:600;color:var(--admin-text);margin:0}.admin-stats{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;margin-bottom:24px}.stat-card{background:var(--admin-card);border-radius:10px;padding:20px;border:1px solid var(--admin-border);display:flex;align-items:center;gap:16px}.stat-icon{width:44px;height:44px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}.stat-icon.blue{background:#eff2ff}.stat-icon.green{background:#f0fdf4}.stat-icon.orange{background:#fff7ed}.stat-icon.red{background:#fff1f2}.stat-info h4{font-size:22px;font-weight:700;color:var(--admin-text);margin:0 0 2px}.stat-info p{font-size:12px;color:var(--admin-muted);margin:0}.admin-table-wrap{overflow-x:auto}.admin-table{width:100%;border-collapse:collapse;font-size:13.5px}.admin-table th{background:#f8fafc;color:var(--admin-muted);font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.04em;padding:10px 14px;border-bottom:1px solid var(--admin-border);text-align:left;white-space:nowrap}.admin-table td{padding:12px 14px;border-bottom:1px solid #f1f5f9;color:var(--admin-text);vertical-align:middle}.admin-table tr:last-child td{border-bottom:none}.admin-table tr:hover td{background:#f8fafc}.admin-table .truncate{max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.badge{display:inline-flex;align-items:center;padding:2px 8px;border-radius:20px;font-size:11px;font-weight:600}.badge-green{background:#dcfce7;color:#16a34a}.badge-red{background:#fee2e2;color:#dc2626}.badge-blue{background:#dbeafe;color:#2563eb}.badge-orange{background:#ffedd5;color:#ea580c}.badge-gray{background:#f1f5f9;color:#64748b}.btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border-radius:7px;font-size:13px;font-weight:500;cursor:pointer;border:none;transition:all .15s;text-decoration:none}.btn-primary{background:var(--admin-accent);color:#fff}.btn-primary:hover{background:var(--admin-accent-hover)}.btn-outline{background:transparent;color:var(--admin-text);border:1px solid var(--admin-border)}.btn-outline:hover{background:#f8fafc}.btn-danger{background:transparent;color:var(--admin-danger);border:1px solid #fecaca}.btn-danger:hover{background:#fff1f2}.btn-sm{padding:5px 10px;font-size:12px}.btn-icon{padding:6px;border-radius:6px}.action-btns{display:flex;gap:6px;align-items:center}.admin-toolbar{display:flex;align-items:center;gap:10px;margin-bottom:16px;flex-wrap:wrap}.admin-toolbar-right{margin-left:auto;display:flex;gap:8px}.admin-search{position:relative}.admin-search input{padding:8px 12px 8px 34px;border:1px solid var(--admin-border);border-radius:7px;font-size:13px;outline:none;width:200px;background:#fff;color:var(--admin-text)}.admin-search input:focus{border-color:var(--admin-accent)}.admin-search-icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--admin-muted);font-size:14px}.admin-select{padding:8px 12px;border:1px solid var(--admin-border);border-radius:7px;font-size:13px;outline:none;background:#fff;color:var(--admin-text);cursor:pointer}.admin-select:focus{border-color:var(--admin-accent)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000073;z-index:1000;display:flex;align-items:center;justify-content:center;padding:16px}.modal{background:var(--admin-card);border-radius:12px;width:100%;max-width:640px;max-height:90vh;overflow-y:auto;box-shadow:0 20px 60px #0003}.modal-header{padding:20px 24px 16px;border-bottom:1px solid var(--admin-border);display:flex;align-items:center;justify-content:space-between}.modal-header h3{font-size:15px;font-weight:600;margin:0}.modal-header button{background:none;border:none;cursor:pointer;color:var(--admin-muted);font-size:18px;line-height:1;padding:4px}.modal-body{padding:24px}.modal-footer{padding:16px 24px;border-top:1px solid var(--admin-border);display:flex;justify-content:flex-end;gap:10px}.form-group{margin-bottom:16px}.form-group label{display:block;font-size:12px;font-weight:600;color:var(--admin-muted);text-transform:uppercase;letter-spacing:.04em;margin-bottom:6px}.form-control{width:100%;padding:9px 12px;border:1px solid var(--admin-border);border-radius:7px;font-size:13.5px;color:var(--admin-text);outline:none;box-sizing:border-box;background:#fff;font-family:inherit}.form-control:focus{border-color:var(--admin-accent);box-shadow:0 0 0 3px #4f6ef71f}textarea.form-control{resize:vertical;min-height:100px}.form-row{display:grid;grid-template-columns:1fr 1fr;gap:14px}.form-check{display:flex;align-items:center;gap:8px;font-size:13.5px;cursor:pointer}.form-check input{width:16px;height:16px;cursor:pointer;accent-color:var(--admin-accent)}.admin-pagination{display:flex;align-items:center;justify-content:space-between;margin-top:16px}.admin-pagination-info{font-size:12px;color:var(--admin-muted)}.pagination-btns{display:flex;gap:4px}.pagination-btns button{padding:5px 10px;border:1px solid var(--admin-border);background:#fff;color:var(--admin-text);border-radius:5px;font-size:12px;cursor:pointer;transition:all .15s}.pagination-btns button:hover:not(:disabled){background:var(--admin-accent);color:#fff;border-color:var(--admin-accent)}.pagination-btns button.active{background:var(--admin-accent);color:#fff;border-color:var(--admin-accent)}.pagination-btns button:disabled{opacity:.4;cursor:not-allowed}.admin-login-page{min-height:100vh;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#1a1d2e,#2a2d4e);padding:16px}.admin-login-box{background:#fff;border-radius:14px;padding:40px 36px;width:100%;max-width:380px;box-shadow:0 20px 60px #0000004d}.admin-login-box .login-logo{text-align:center;margin-bottom:28px}.admin-login-box .login-logo h1{font-size:22px;font-weight:800;color:var(--admin-text);margin:8px 0 4px}.admin-login-box .login-logo p{font-size:12px;color:var(--admin-muted);margin:0}.admin-login-box .login-badge{display:inline-block;background:#eff2ff;color:var(--admin-accent);padding:2px 10px;border-radius:20px;font-size:11px;font-weight:700;margin-bottom:6px}.login-input-group{margin-bottom:14px}.login-input-group label{display:block;font-size:12px;font-weight:600;color:var(--admin-muted);margin-bottom:6px}.login-input-group input{width:100%;padding:11px 14px;border:1.5px solid var(--admin-border);border-radius:8px;font-size:14px;outline:none;box-sizing:border-box;transition:border-color .15s}.login-input-group input:focus{border-color:var(--admin-accent)}.login-btn{width:100%;padding:12px;background:var(--admin-accent);color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;margin-top:6px;transition:background .15s}.login-btn:hover{background:var(--admin-accent-hover)}.login-error{background:#fff1f2;color:var(--admin-danger);font-size:12.5px;padding:9px 12px;border-radius:7px;margin-bottom:14px;border:1px solid #fecaca}.recent-list{list-style:none;padding:0;margin:0}.recent-list li{display:flex;align-items:center;gap:10px;padding:10px 0;border-bottom:1px solid #f1f5f9;font-size:13px}.recent-list li:last-child{border-bottom:none}.recent-list .rl-dot{width:7px;height:7px;border-radius:50%;background:var(--admin-accent);flex-shrink:0}.recent-list .rl-title{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--admin-text)}.recent-list .rl-meta{color:var(--admin-muted);font-size:11px;white-space:nowrap}.empty-state{text-align:center;padding:48px 0;color:var(--admin-muted)}.empty-state .empty-icon{font-size:40px;margin-bottom:12px}.empty-state p{font-size:13.5px;margin:0}.admin-toast{position:fixed;bottom:24px;right:24px;z-index:9999;display:flex;flex-direction:column;gap:8px}.toast-item{background:#1e293b;color:#fff;padding:12px 20px;border-radius:8px;font-size:13px;animation:slideUp .2s ease;box-shadow:0 4px 16px #0003}.toast-item.success{border-left:3px solid var(--admin-success)}.toast-item.error{border-left:3px solid var(--admin-danger)}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@media (max-width: 768px){.admin-sidebar{transform:translate(-100%)}.admin-sidebar.open{transform:translate(0)}.admin-main{margin-left:0}.form-row{grid-template-columns:1fr}.admin-stats{grid-template-columns:1fr 1fr}} diff --git a/backend/src/main/resources/static/assets/index-ChpGil2q.js b/backend/src/main/resources/static/assets/index-ChpGil2q.js new file mode 100644 index 0000000..f796ab6 --- /dev/null +++ b/backend/src/main/resources/static/assets/index-ChpGil2q.js @@ -0,0 +1,68 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Home-BC38QtTl.js","assets/index-DcNlVx-A.js","assets/Home-jagO1aR4.css","assets/GuardiaDetail-5Pm8bk4O.js","assets/GuardiaDetail-Ax0ubrjA.css","assets/SolutionPage-Da0Vpoc-.js","assets/SolutionPage-C7_Wcm8g.css","assets/Common-BmvLh5lB.css","assets/Company-BOdWAIQ4.js","assets/Company-qD6qaVvP.css","assets/Business-EGnXphuY.js","assets/Business-EM7OeA4c.css","assets/Contact-C6p_tBWi.js","assets/Contact-C2ZwoM3_.css","assets/Support-C5QVP1gW.js","assets/Support-DvGESosS.css","assets/NewsPage-mgytOZhS.js","assets/NewsPage-BgXQ2CUT.css","assets/Recruit-DlKGM6KQ.js","assets/Recruit-CbW65yqF.css","assets/AdminLogin-DcRT5LbX.js","assets/admin-BHL-7hu0.css","assets/AdminLayout-uWX9KsdF.js"])))=>i.map(i=>d[i]); +function tf(e,t){for(var n=0;nr[l]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const o of l)if(o.type==="childList")for(const i of o.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&r(i)}).observe(document,{childList:!0,subtree:!0});function n(l){const o={};return l.integrity&&(o.integrity=l.integrity),l.referrerPolicy&&(o.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?o.credentials="include":l.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function r(l){if(l.ep)return;l.ep=!0;const o=n(l);fetch(l.href,o)}})();function nf(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var La={exports:{}},vl={},Ra={exports:{}},O={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var ar=Symbol.for("react.element"),rf=Symbol.for("react.portal"),lf=Symbol.for("react.fragment"),of=Symbol.for("react.strict_mode"),uf=Symbol.for("react.profiler"),af=Symbol.for("react.provider"),sf=Symbol.for("react.context"),cf=Symbol.for("react.forward_ref"),ff=Symbol.for("react.suspense"),df=Symbol.for("react.memo"),pf=Symbol.for("react.lazy"),uu=Symbol.iterator;function hf(e){return e===null||typeof e!="object"?null:(e=uu&&e[uu]||e["@@iterator"],typeof e=="function"?e:null)}var ja={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},za=Object.assign,Ta={};function mn(e,t,n){this.props=e,this.context=t,this.refs=Ta,this.updater=n||ja}mn.prototype.isReactComponent={};mn.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};mn.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Oa(){}Oa.prototype=mn.prototype;function si(e,t,n){this.props=e,this.context=t,this.refs=Ta,this.updater=n||ja}var ci=si.prototype=new Oa;ci.constructor=si;za(ci,mn.prototype);ci.isPureReactComponent=!0;var au=Array.isArray,Ia=Object.prototype.hasOwnProperty,fi={current:null},Da={key:!0,ref:!0,__self:!0,__source:!0};function Ma(e,t,n){var r,l={},o=null,i=null;if(t!=null)for(r in t.ref!==void 0&&(i=t.ref),t.key!==void 0&&(o=""+t.key),t)Ia.call(t,r)&&!Da.hasOwnProperty(r)&&(l[r]=t[r]);var u=arguments.length-2;if(u===1)l.children=n;else if(1>>1,q=P[K];if(0>>1;Kl(Fl,T))Ptl(hr,Fl)?(P[K]=hr,P[Pt]=T,K=Pt):(P[K]=Fl,P[Ct]=T,K=Ct);else if(Ptl(hr,T))P[K]=hr,P[Pt]=T,K=Pt;else break e}}return j}function l(P,j){var T=P.sortIndex-j.sortIndex;return T!==0?T:P.id-j.id}if(typeof performance=="object"&&typeof performance.now=="function"){var o=performance;e.unstable_now=function(){return o.now()}}else{var i=Date,u=i.now();e.unstable_now=function(){return i.now()-u}}var a=[],s=[],h=1,p=null,m=3,S=!1,w=!1,k=!1,_=typeof setTimeout=="function"?setTimeout:null,f=typeof clearTimeout=="function"?clearTimeout:null,c=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function d(P){for(var j=n(s);j!==null;){if(j.callback===null)r(s);else if(j.startTime<=P)r(s),j.sortIndex=j.expirationTime,t(a,j);else break;j=n(s)}}function v(P){if(k=!1,d(P),!w)if(n(a)!==null)w=!0,Dl(E);else{var j=n(s);j!==null&&Ml(v,j.startTime-P)}}function E(P,j){w=!1,k&&(k=!1,f(R),R=-1),S=!0;var T=m;try{for(d(j),p=n(a);p!==null&&(!(p.expirationTime>j)||P&&!ge());){var K=p.callback;if(typeof K=="function"){p.callback=null,m=p.priorityLevel;var q=K(p.expirationTime<=j);j=e.unstable_now(),typeof q=="function"?p.callback=q:p===n(a)&&r(a),d(j)}else r(a);p=n(a)}if(p!==null)var pr=!0;else{var Ct=n(s);Ct!==null&&Ml(v,Ct.startTime-j),pr=!1}return pr}finally{p=null,m=T,S=!1}}var N=!1,L=null,R=-1,$=5,z=-1;function ge(){return!(e.unstable_now()-z<$)}function Sn(){if(L!==null){var P=e.unstable_now();z=P;var j=!0;try{j=L(!0,P)}finally{j?kn():(N=!1,L=null)}}else N=!1}var kn;if(typeof c=="function")kn=function(){c(Sn)};else if(typeof MessageChannel<"u"){var iu=new MessageChannel,ef=iu.port2;iu.port1.onmessage=Sn,kn=function(){ef.postMessage(null)}}else kn=function(){_(Sn,0)};function Dl(P){L=P,N||(N=!0,kn())}function Ml(P,j){R=_(function(){P(e.unstable_now())},j)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(P){P.callback=null},e.unstable_continueExecution=function(){w||S||(w=!0,Dl(E))},e.unstable_forceFrameRate=function(P){0>P||125K?(P.sortIndex=T,t(s,P),n(a)===null&&P===n(s)&&(k?(f(R),R=-1):k=!0,Ml(v,T-K))):(P.sortIndex=q,t(a,P),w||S||(w=!0,Dl(E))),P},e.unstable_shouldYield=ge,e.unstable_wrapCallback=function(P){var j=m;return function(){var T=m;m=j;try{return P.apply(this,arguments)}finally{m=T}}}})(Va);Ba.exports=Va;var Pf=Ba.exports;/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Nf=g,xe=Pf;function x(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),po=Object.prototype.hasOwnProperty,Lf=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,cu={},fu={};function Rf(e){return po.call(fu,e)?!0:po.call(cu,e)?!1:Lf.test(e)?fu[e]=!0:(cu[e]=!0,!1)}function jf(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function zf(e,t,n,r){if(t===null||typeof t>"u"||jf(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function de(e,t,n,r,l,o,i){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=o,this.removeEmptyString=i}var re={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){re[e]=new de(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];re[t]=new de(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){re[e]=new de(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){re[e]=new de(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){re[e]=new de(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){re[e]=new de(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){re[e]=new de(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){re[e]=new de(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){re[e]=new de(e,5,!1,e.toLowerCase(),null,!1,!1)});var pi=/[\-:]([a-z])/g;function hi(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(pi,hi);re[t]=new de(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(pi,hi);re[t]=new de(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(pi,hi);re[t]=new de(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){re[e]=new de(e,1,!1,e.toLowerCase(),null,!1,!1)});re.xlinkHref=new de("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){re[e]=new de(e,1,!1,e.toLowerCase(),null,!0,!0)});function mi(e,t,n,r){var l=re.hasOwnProperty(t)?re[t]:null;(l!==null?l.type!==0:r||!(2u||l[i]!==o[u]){var a=` +`+l[i].replace(" at new "," at ");return e.displayName&&a.includes("")&&(a=a.replace("",e.displayName)),a}while(1<=i&&0<=u);break}}}finally{$l=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?jn(e):""}function Tf(e){switch(e.tag){case 5:return jn(e.type);case 16:return jn("Lazy");case 13:return jn("Suspense");case 19:return jn("SuspenseList");case 0:case 2:case 15:return e=Bl(e.type,!1),e;case 11:return e=Bl(e.type.render,!1),e;case 1:return e=Bl(e.type,!0),e;default:return""}}function yo(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Wt:return"Fragment";case Vt:return"Portal";case ho:return"Profiler";case vi:return"StrictMode";case mo:return"Suspense";case vo:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Qa:return(e.displayName||"Context")+".Consumer";case Ha:return(e._context.displayName||"Context")+".Provider";case yi:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case gi:return t=e.displayName||null,t!==null?t:yo(e.type)||"Memo";case rt:t=e._payload,e=e._init;try{return yo(e(t))}catch{}}return null}function Of(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return yo(t);case 8:return t===vi?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function wt(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Ya(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function If(e){var t=Ya(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var l=n.get,o=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(i){r=""+i,o.call(this,i)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(i){r=""+i},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function yr(e){e._valueTracker||(e._valueTracker=If(e))}function Ga(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Ya(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function Hr(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function go(e,t){var n=t.checked;return H({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function pu(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=wt(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Xa(e,t){t=t.checked,t!=null&&mi(e,"checked",t,!1)}function wo(e,t){Xa(e,t);var n=wt(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?So(e,t.type,n):t.hasOwnProperty("defaultValue")&&So(e,t.type,wt(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function hu(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function So(e,t,n){(t!=="number"||Hr(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var zn=Array.isArray;function en(e,t,n,r){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=gr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Hn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var In={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Df=["Webkit","ms","Moz","O"];Object.keys(In).forEach(function(e){Df.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),In[t]=In[e]})});function ba(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||In.hasOwnProperty(e)&&In[e]?(""+t).trim():t+"px"}function es(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,l=ba(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,l):e[n]=l}}var Mf=H({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Eo(e,t){if(t){if(Mf[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(x(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(x(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(x(61))}if(t.style!=null&&typeof t.style!="object")throw Error(x(62))}}function _o(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Co=null;function wi(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Po=null,tn=null,nn=null;function yu(e){if(e=fr(e)){if(typeof Po!="function")throw Error(x(280));var t=e.stateNode;t&&(t=kl(t),Po(e.stateNode,e.type,t))}}function ts(e){tn?nn?nn.push(e):nn=[e]:tn=e}function ns(){if(tn){var e=tn,t=nn;if(nn=tn=null,yu(e),t)for(e=0;e>>=0,e===0?32:31-(Yf(e)/Gf|0)|0}var wr=64,Sr=4194304;function Tn(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Gr(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,l=e.suspendedLanes,o=e.pingedLanes,i=n&268435455;if(i!==0){var u=i&~l;u!==0?r=Tn(u):(o&=i,o!==0&&(r=Tn(o)))}else i=n&~l,i!==0?r=Tn(i):o!==0&&(r=Tn(o));if(r===0)return 0;if(t!==0&&t!==r&&!(t&l)&&(l=r&-r,o=t&-t,l>=o||l===16&&(o&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function sr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Me(t),e[t]=n}function qf(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=Mn),Pu=" ",Nu=!1;function xs(e,t){switch(e){case"keyup":return Pd.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Es(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Ht=!1;function Ld(e,t){switch(e){case"compositionend":return Es(t);case"keypress":return t.which!==32?null:(Nu=!0,Pu);case"textInput":return e=t.data,e===Pu&&Nu?null:e;default:return null}}function Rd(e,t){if(Ht)return e==="compositionend"||!Ni&&xs(e,t)?(e=Ss(),Dr=_i=ut=null,Ht=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=zu(n)}}function Ns(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Ns(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Ls(){for(var e=window,t=Hr();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Hr(e.document)}return t}function Li(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Ud(e){var t=Ls(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Ns(n.ownerDocument.documentElement,n)){if(r!==null&&Li(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=n.textContent.length,o=Math.min(r.start,l);r=r.end===void 0?o:Math.min(r.end,l),!e.extend&&o>r&&(l=r,r=o,o=l),l=Tu(n,o);var i=Tu(n,r);l&&i&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==i.node||e.focusOffset!==i.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),o>r?(e.addRange(t),e.extend(i.node,i.offset)):(t.setEnd(i.node,i.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Qt=null,To=null,Un=null,Oo=!1;function Ou(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Oo||Qt==null||Qt!==Hr(r)||(r=Qt,"selectionStart"in r&&Li(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Un&&Zn(Un,r)||(Un=r,r=Jr(To,"onSelect"),0Gt||(e.current=Ao[Gt],Ao[Gt]=null,Gt--)}function M(e,t){Gt++,Ao[Gt]=e.current,e.current=t}var St={},ae=xt(St),me=xt(!1),It=St;function an(e,t){var n=e.type.contextTypes;if(!n)return St;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var l={},o;for(o in n)l[o]=t[o];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function ve(e){return e=e.childContextTypes,e!=null}function br(){U(me),U(ae)}function $u(e,t,n){if(ae.current!==St)throw Error(x(168));M(ae,t),M(me,n)}function Fs(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var l in r)if(!(l in t))throw Error(x(108,Of(e)||"Unknown",l));return H({},n,r)}function el(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||St,It=ae.current,M(ae,e),M(me,me.current),!0}function Bu(e,t,n){var r=e.stateNode;if(!r)throw Error(x(169));n?(e=Fs(e,t,It),r.__reactInternalMemoizedMergedChildContext=e,U(me),U(ae),M(ae,e)):U(me),M(me,n)}var Qe=null,xl=!1,to=!1;function Us(e){Qe===null?Qe=[e]:Qe.push(e)}function Zd(e){xl=!0,Us(e)}function Et(){if(!to&&Qe!==null){to=!0;var e=0,t=D;try{var n=Qe;for(D=1;e>=i,l-=i,Ke=1<<32-Me(t)+l|n<R?($=L,L=null):$=L.sibling;var z=m(f,L,d[R],v);if(z===null){L===null&&(L=$);break}e&&L&&z.alternate===null&&t(f,L),c=o(z,c,R),N===null?E=z:N.sibling=z,N=z,L=$}if(R===d.length)return n(f,L),A&&Nt(f,R),E;if(L===null){for(;RR?($=L,L=null):$=L.sibling;var ge=m(f,L,z.value,v);if(ge===null){L===null&&(L=$);break}e&&L&&ge.alternate===null&&t(f,L),c=o(ge,c,R),N===null?E=ge:N.sibling=ge,N=ge,L=$}if(z.done)return n(f,L),A&&Nt(f,R),E;if(L===null){for(;!z.done;R++,z=d.next())z=p(f,z.value,v),z!==null&&(c=o(z,c,R),N===null?E=z:N.sibling=z,N=z);return A&&Nt(f,R),E}for(L=r(f,L);!z.done;R++,z=d.next())z=S(L,f,R,z.value,v),z!==null&&(e&&z.alternate!==null&&L.delete(z.key===null?R:z.key),c=o(z,c,R),N===null?E=z:N.sibling=z,N=z);return e&&L.forEach(function(Sn){return t(f,Sn)}),A&&Nt(f,R),E}function _(f,c,d,v){if(typeof d=="object"&&d!==null&&d.type===Wt&&d.key===null&&(d=d.props.children),typeof d=="object"&&d!==null){switch(d.$$typeof){case vr:e:{for(var E=d.key,N=c;N!==null;){if(N.key===E){if(E=d.type,E===Wt){if(N.tag===7){n(f,N.sibling),c=l(N,d.props.children),c.return=f,f=c;break e}}else if(N.elementType===E||typeof E=="object"&&E!==null&&E.$$typeof===rt&&Hu(E)===N.type){n(f,N.sibling),c=l(N,d.props),c.ref=Nn(f,N,d),c.return=f,f=c;break e}n(f,N);break}else t(f,N);N=N.sibling}d.type===Wt?(c=Ot(d.props.children,f.mode,v,d.key),c.return=f,f=c):(v=Wr(d.type,d.key,d.props,null,f.mode,v),v.ref=Nn(f,c,d),v.return=f,f=v)}return i(f);case Vt:e:{for(N=d.key;c!==null;){if(c.key===N)if(c.tag===4&&c.stateNode.containerInfo===d.containerInfo&&c.stateNode.implementation===d.implementation){n(f,c.sibling),c=l(c,d.children||[]),c.return=f,f=c;break e}else{n(f,c);break}else t(f,c);c=c.sibling}c=so(d,f.mode,v),c.return=f,f=c}return i(f);case rt:return N=d._init,_(f,c,N(d._payload),v)}if(zn(d))return w(f,c,d,v);if(xn(d))return k(f,c,d,v);Nr(f,d)}return typeof d=="string"&&d!==""||typeof d=="number"?(d=""+d,c!==null&&c.tag===6?(n(f,c.sibling),c=l(c,d),c.return=f,f=c):(n(f,c),c=ao(d,f.mode,v),c.return=f,f=c),i(f)):n(f,c)}return _}var cn=Vs(!0),Ws=Vs(!1),rl=xt(null),ll=null,Jt=null,Ti=null;function Oi(){Ti=Jt=ll=null}function Ii(e){var t=rl.current;U(rl),e._currentValue=t}function Vo(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function ln(e,t){ll=e,Ti=Jt=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(he=!0),e.firstContext=null)}function Re(e){var t=e._currentValue;if(Ti!==e)if(e={context:e,memoizedValue:t,next:null},Jt===null){if(ll===null)throw Error(x(308));Jt=e,ll.dependencies={lanes:0,firstContext:e}}else Jt=Jt.next=e;return t}var jt=null;function Di(e){jt===null?jt=[e]:jt.push(e)}function Hs(e,t,n,r){var l=t.interleaved;return l===null?(n.next=n,Di(t)):(n.next=l.next,l.next=n),t.interleaved=n,Je(e,r)}function Je(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var lt=!1;function Mi(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Qs(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Ge(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function ht(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,I&2){var l=r.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),r.pending=t,Je(e,n)}return l=r.interleaved,l===null?(t.next=t,Di(r)):(t.next=l.next,l.next=t),r.interleaved=t,Je(e,n)}function Fr(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ki(e,n)}}function Qu(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var l=null,o=null;if(n=n.firstBaseUpdate,n!==null){do{var i={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};o===null?l=o=i:o=o.next=i,n=n.next}while(n!==null);o===null?l=o=t:o=o.next=t}else l=o=t;n={baseState:r.baseState,firstBaseUpdate:l,lastBaseUpdate:o,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function ol(e,t,n,r){var l=e.updateQueue;lt=!1;var o=l.firstBaseUpdate,i=l.lastBaseUpdate,u=l.shared.pending;if(u!==null){l.shared.pending=null;var a=u,s=a.next;a.next=null,i===null?o=s:i.next=s,i=a;var h=e.alternate;h!==null&&(h=h.updateQueue,u=h.lastBaseUpdate,u!==i&&(u===null?h.firstBaseUpdate=s:u.next=s,h.lastBaseUpdate=a))}if(o!==null){var p=l.baseState;i=0,h=s=a=null,u=o;do{var m=u.lane,S=u.eventTime;if((r&m)===m){h!==null&&(h=h.next={eventTime:S,lane:0,tag:u.tag,payload:u.payload,callback:u.callback,next:null});e:{var w=e,k=u;switch(m=t,S=n,k.tag){case 1:if(w=k.payload,typeof w=="function"){p=w.call(S,p,m);break e}p=w;break e;case 3:w.flags=w.flags&-65537|128;case 0:if(w=k.payload,m=typeof w=="function"?w.call(S,p,m):w,m==null)break e;p=H({},p,m);break e;case 2:lt=!0}}u.callback!==null&&u.lane!==0&&(e.flags|=64,m=l.effects,m===null?l.effects=[u]:m.push(u))}else S={eventTime:S,lane:m,tag:u.tag,payload:u.payload,callback:u.callback,next:null},h===null?(s=h=S,a=p):h=h.next=S,i|=m;if(u=u.next,u===null){if(u=l.shared.pending,u===null)break;m=u,u=m.next,m.next=null,l.lastBaseUpdate=m,l.shared.pending=null}}while(!0);if(h===null&&(a=p),l.baseState=a,l.firstBaseUpdate=s,l.lastBaseUpdate=h,t=l.shared.interleaved,t!==null){l=t;do i|=l.lane,l=l.next;while(l!==t)}else o===null&&(l.shared.lanes=0);Ft|=i,e.lanes=i,e.memoizedState=p}}function Ku(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=ro.transition;ro.transition={};try{e(!1),t()}finally{D=n,ro.transition=r}}function ac(){return je().memoizedState}function ep(e,t,n){var r=vt(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},sc(e))cc(t,n);else if(n=Hs(e,t,n,r),n!==null){var l=ce();Fe(n,e,r,l),fc(n,t,r)}}function tp(e,t,n){var r=vt(e),l={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(sc(e))cc(t,l);else{var o=e.alternate;if(e.lanes===0&&(o===null||o.lanes===0)&&(o=t.lastRenderedReducer,o!==null))try{var i=t.lastRenderedState,u=o(i,n);if(l.hasEagerState=!0,l.eagerState=u,Ue(u,i)){var a=t.interleaved;a===null?(l.next=l,Di(t)):(l.next=a.next,a.next=l),t.interleaved=l;return}}catch{}finally{}n=Hs(e,t,l,r),n!==null&&(l=ce(),Fe(n,e,r,l),fc(n,t,r))}}function sc(e){var t=e.alternate;return e===V||t!==null&&t===V}function cc(e,t){An=ul=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function fc(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ki(e,n)}}var al={readContext:Re,useCallback:oe,useContext:oe,useEffect:oe,useImperativeHandle:oe,useInsertionEffect:oe,useLayoutEffect:oe,useMemo:oe,useReducer:oe,useRef:oe,useState:oe,useDebugValue:oe,useDeferredValue:oe,useTransition:oe,useMutableSource:oe,useSyncExternalStore:oe,useId:oe,unstable_isNewReconciler:!1},np={readContext:Re,useCallback:function(e,t){return $e().memoizedState=[e,t===void 0?null:t],e},useContext:Re,useEffect:Gu,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Ar(4194308,4,rc.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Ar(4194308,4,e,t)},useInsertionEffect:function(e,t){return Ar(4,2,e,t)},useMemo:function(e,t){var n=$e();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=$e();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=ep.bind(null,V,e),[r.memoizedState,e]},useRef:function(e){var t=$e();return e={current:e},t.memoizedState=e},useState:Yu,useDebugValue:Hi,useDeferredValue:function(e){return $e().memoizedState=e},useTransition:function(){var e=Yu(!1),t=e[0];return e=bd.bind(null,e[1]),$e().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=V,l=$e();if(A){if(n===void 0)throw Error(x(407));n=n()}else{if(n=t(),ee===null)throw Error(x(349));Mt&30||Xs(r,t,n)}l.memoizedState=n;var o={value:n,getSnapshot:t};return l.queue=o,Gu(Js.bind(null,r,o,e),[e]),r.flags|=2048,lr(9,Zs.bind(null,r,o,n,t),void 0,null),n},useId:function(){var e=$e(),t=ee.identifierPrefix;if(A){var n=Ye,r=Ke;n=(r&~(1<<32-Me(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=nr++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=i.createElement(n,{is:r.is}):(e=i.createElement(n),n==="select"&&(i=e,r.multiple?i.multiple=!0:r.size&&(i.size=r.size))):e=i.createElementNS(e,n),e[Be]=t,e[bn]=r,kc(e,t,!1,!1),t.stateNode=e;e:{switch(i=_o(n,r),n){case"dialog":F("cancel",e),F("close",e),l=r;break;case"iframe":case"object":case"embed":F("load",e),l=r;break;case"video":case"audio":for(l=0;lpn&&(t.flags|=128,r=!0,Ln(o,!1),t.lanes=4194304)}else{if(!r)if(e=il(i),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Ln(o,!0),o.tail===null&&o.tailMode==="hidden"&&!i.alternate&&!A)return ie(t),null}else 2*Y()-o.renderingStartTime>pn&&n!==1073741824&&(t.flags|=128,r=!0,Ln(o,!1),t.lanes=4194304);o.isBackwards?(i.sibling=t.child,t.child=i):(n=o.last,n!==null?n.sibling=i:t.child=i,o.last=i)}return o.tail!==null?(t=o.tail,o.rendering=t,o.tail=t.sibling,o.renderingStartTime=Y(),t.sibling=null,n=B.current,M(B,r?n&1|2:n&1),t):(ie(t),null);case 22:case 23:return Zi(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?we&1073741824&&(ie(t),t.subtreeFlags&6&&(t.flags|=8192)):ie(t),null;case 24:return null;case 25:return null}throw Error(x(156,t.tag))}function cp(e,t){switch(ji(t),t.tag){case 1:return ve(t.type)&&br(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return fn(),U(me),U(ae),Ai(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return Ui(t),null;case 13:if(U(B),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(x(340));sn()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return U(B),null;case 4:return fn(),null;case 10:return Ii(t.type._context),null;case 22:case 23:return Zi(),null;case 24:return null;default:return null}}var Rr=!1,ue=!1,fp=typeof WeakSet=="function"?WeakSet:Set,C=null;function qt(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){Q(e,t,r)}else n.current=null}function Jo(e,t,n){try{n()}catch(r){Q(e,t,r)}}var oa=!1;function dp(e,t){if(Io=Xr,e=Ls(),Li(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var l=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break e}var i=0,u=-1,a=-1,s=0,h=0,p=e,m=null;t:for(;;){for(var S;p!==n||l!==0&&p.nodeType!==3||(u=i+l),p!==o||r!==0&&p.nodeType!==3||(a=i+r),p.nodeType===3&&(i+=p.nodeValue.length),(S=p.firstChild)!==null;)m=p,p=S;for(;;){if(p===e)break t;if(m===n&&++s===l&&(u=i),m===o&&++h===r&&(a=i),(S=p.nextSibling)!==null)break;p=m,m=p.parentNode}p=S}n=u===-1||a===-1?null:{start:u,end:a}}else n=null}n=n||{start:0,end:0}}else n=null;for(Do={focusedElem:e,selectionRange:n},Xr=!1,C=t;C!==null;)if(t=C,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,C=e;else for(;C!==null;){t=C;try{var w=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(w!==null){var k=w.memoizedProps,_=w.memoizedState,f=t.stateNode,c=f.getSnapshotBeforeUpdate(t.elementType===t.type?k:Te(t.type,k),_);f.__reactInternalSnapshotBeforeUpdate=c}break;case 3:var d=t.stateNode.containerInfo;d.nodeType===1?d.textContent="":d.nodeType===9&&d.documentElement&&d.removeChild(d.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(x(163))}}catch(v){Q(t,t.return,v)}if(e=t.sibling,e!==null){e.return=t.return,C=e;break}C=t.return}return w=oa,oa=!1,w}function $n(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&e)===e){var o=l.destroy;l.destroy=void 0,o!==void 0&&Jo(t,n,o)}l=l.next}while(l!==r)}}function Cl(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function qo(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function _c(e){var t=e.alternate;t!==null&&(e.alternate=null,_c(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Be],delete t[bn],delete t[Uo],delete t[Gd],delete t[Xd])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function Cc(e){return e.tag===5||e.tag===3||e.tag===4}function ia(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Cc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function bo(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=qr));else if(r!==4&&(e=e.child,e!==null))for(bo(e,t,n),e=e.sibling;e!==null;)bo(e,t,n),e=e.sibling}function ei(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(ei(e,t,n),e=e.sibling;e!==null;)ei(e,t,n),e=e.sibling}var te=null,Oe=!1;function nt(e,t,n){for(n=n.child;n!==null;)Pc(e,t,n),n=n.sibling}function Pc(e,t,n){if(Ve&&typeof Ve.onCommitFiberUnmount=="function")try{Ve.onCommitFiberUnmount(yl,n)}catch{}switch(n.tag){case 5:ue||qt(n,t);case 6:var r=te,l=Oe;te=null,nt(e,t,n),te=r,Oe=l,te!==null&&(Oe?(e=te,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):te.removeChild(n.stateNode));break;case 18:te!==null&&(Oe?(e=te,n=n.stateNode,e.nodeType===8?eo(e.parentNode,n):e.nodeType===1&&eo(e,n),Gn(e)):eo(te,n.stateNode));break;case 4:r=te,l=Oe,te=n.stateNode.containerInfo,Oe=!0,nt(e,t,n),te=r,Oe=l;break;case 0:case 11:case 14:case 15:if(!ue&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){l=r=r.next;do{var o=l,i=o.destroy;o=o.tag,i!==void 0&&(o&2||o&4)&&Jo(n,t,i),l=l.next}while(l!==r)}nt(e,t,n);break;case 1:if(!ue&&(qt(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(u){Q(n,t,u)}nt(e,t,n);break;case 21:nt(e,t,n);break;case 22:n.mode&1?(ue=(r=ue)||n.memoizedState!==null,nt(e,t,n),ue=r):nt(e,t,n);break;default:nt(e,t,n)}}function ua(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new fp),t.forEach(function(r){var l=kp.bind(null,e,r);n.has(r)||(n.add(r),r.then(l,l))})}}function ze(e,t){var n=t.deletions;if(n!==null)for(var r=0;rl&&(l=i),r&=~o}if(r=l,r=Y()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*hp(r/1960))-r,10e?16:e,at===null)var r=!1;else{if(e=at,at=null,fl=0,I&6)throw Error(x(331));var l=I;for(I|=4,C=e.current;C!==null;){var o=C,i=o.child;if(C.flags&16){var u=o.deletions;if(u!==null){for(var a=0;aY()-Gi?Tt(e,0):Yi|=n),ye(e,t)}function Ic(e,t){t===0&&(e.mode&1?(t=Sr,Sr<<=1,!(Sr&130023424)&&(Sr=4194304)):t=1);var n=ce();e=Je(e,t),e!==null&&(sr(e,t,n),ye(e,n))}function Sp(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Ic(e,n)}function kp(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,l=e.memoizedState;l!==null&&(n=l.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(x(314))}r!==null&&r.delete(t),Ic(e,n)}var Dc;Dc=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||me.current)he=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return he=!1,ap(e,t,n);he=!!(e.flags&131072)}else he=!1,A&&t.flags&1048576&&As(t,nl,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;$r(e,t),e=t.pendingProps;var l=an(t,ae.current);ln(t,n),l=Bi(null,t,r,e,l,n);var o=Vi();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,ve(r)?(o=!0,el(t)):o=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,Mi(t),l.updater=_l,t.stateNode=l,l._reactInternals=t,Ho(t,r,e,n),t=Yo(null,t,r,!0,o,n)):(t.tag=0,A&&o&&Ri(t),se(null,t,l,n),t=t.child),t;case 16:r=t.elementType;e:{switch($r(e,t),e=t.pendingProps,l=r._init,r=l(r._payload),t.type=r,l=t.tag=Ep(r),e=Te(r,e),l){case 0:t=Ko(null,t,r,e,n);break e;case 1:t=na(null,t,r,e,n);break e;case 11:t=ea(null,t,r,e,n);break e;case 14:t=ta(null,t,r,Te(r.type,e),n);break e}throw Error(x(306,r,""))}return t;case 0:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:Te(r,l),Ko(e,t,r,l,n);case 1:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:Te(r,l),na(e,t,r,l,n);case 3:e:{if(gc(t),e===null)throw Error(x(387));r=t.pendingProps,o=t.memoizedState,l=o.element,Qs(e,t),ol(t,r,null,n);var i=t.memoizedState;if(r=i.element,o.isDehydrated)if(o={element:r,isDehydrated:!1,cache:i.cache,pendingSuspenseBoundaries:i.pendingSuspenseBoundaries,transitions:i.transitions},t.updateQueue.baseState=o,t.memoizedState=o,t.flags&256){l=dn(Error(x(423)),t),t=ra(e,t,r,n,l);break e}else if(r!==l){l=dn(Error(x(424)),t),t=ra(e,t,r,n,l);break e}else for(Se=pt(t.stateNode.containerInfo.firstChild),ke=t,A=!0,Ie=null,n=Ws(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(sn(),r===l){t=qe(e,t,n);break e}se(e,t,r,n)}t=t.child}return t;case 5:return Ks(t),e===null&&Bo(t),r=t.type,l=t.pendingProps,o=e!==null?e.memoizedProps:null,i=l.children,Mo(r,l)?i=null:o!==null&&Mo(r,o)&&(t.flags|=32),yc(e,t),se(e,t,i,n),t.child;case 6:return e===null&&Bo(t),null;case 13:return wc(e,t,n);case 4:return Fi(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=cn(t,null,r,n):se(e,t,r,n),t.child;case 11:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:Te(r,l),ea(e,t,r,l,n);case 7:return se(e,t,t.pendingProps,n),t.child;case 8:return se(e,t,t.pendingProps.children,n),t.child;case 12:return se(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,l=t.pendingProps,o=t.memoizedProps,i=l.value,M(rl,r._currentValue),r._currentValue=i,o!==null)if(Ue(o.value,i)){if(o.children===l.children&&!me.current){t=qe(e,t,n);break e}}else for(o=t.child,o!==null&&(o.return=t);o!==null;){var u=o.dependencies;if(u!==null){i=o.child;for(var a=u.firstContext;a!==null;){if(a.context===r){if(o.tag===1){a=Ge(-1,n&-n),a.tag=2;var s=o.updateQueue;if(s!==null){s=s.shared;var h=s.pending;h===null?a.next=a:(a.next=h.next,h.next=a),s.pending=a}}o.lanes|=n,a=o.alternate,a!==null&&(a.lanes|=n),Vo(o.return,n,t),u.lanes|=n;break}a=a.next}}else if(o.tag===10)i=o.type===t.type?null:o.child;else if(o.tag===18){if(i=o.return,i===null)throw Error(x(341));i.lanes|=n,u=i.alternate,u!==null&&(u.lanes|=n),Vo(i,n,t),i=o.sibling}else i=o.child;if(i!==null)i.return=o;else for(i=o;i!==null;){if(i===t){i=null;break}if(o=i.sibling,o!==null){o.return=i.return,i=o;break}i=i.return}o=i}se(e,t,l.children,n),t=t.child}return t;case 9:return l=t.type,r=t.pendingProps.children,ln(t,n),l=Re(l),r=r(l),t.flags|=1,se(e,t,r,n),t.child;case 14:return r=t.type,l=Te(r,t.pendingProps),l=Te(r.type,l),ta(e,t,r,l,n);case 15:return mc(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:Te(r,l),$r(e,t),t.tag=1,ve(r)?(e=!0,el(t)):e=!1,ln(t,n),dc(t,r,l),Ho(t,r,l,n),Yo(null,t,r,!0,e,n);case 19:return Sc(e,t,n);case 22:return vc(e,t,n)}throw Error(x(156,t.tag))};function Mc(e,t){return ss(e,t)}function xp(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Ne(e,t,n,r){return new xp(e,t,n,r)}function qi(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Ep(e){if(typeof e=="function")return qi(e)?1:0;if(e!=null){if(e=e.$$typeof,e===yi)return 11;if(e===gi)return 14}return 2}function yt(e,t){var n=e.alternate;return n===null?(n=Ne(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Wr(e,t,n,r,l,o){var i=2;if(r=e,typeof e=="function")qi(e)&&(i=1);else if(typeof e=="string")i=5;else e:switch(e){case Wt:return Ot(n.children,l,o,t);case vi:i=8,l|=8;break;case ho:return e=Ne(12,n,t,l|2),e.elementType=ho,e.lanes=o,e;case mo:return e=Ne(13,n,t,l),e.elementType=mo,e.lanes=o,e;case vo:return e=Ne(19,n,t,l),e.elementType=vo,e.lanes=o,e;case Ka:return Nl(n,l,o,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Ha:i=10;break e;case Qa:i=9;break e;case yi:i=11;break e;case gi:i=14;break e;case rt:i=16,r=null;break e}throw Error(x(130,e==null?e:typeof e,""))}return t=Ne(i,n,t,l),t.elementType=e,t.type=r,t.lanes=o,t}function Ot(e,t,n,r){return e=Ne(7,e,r,t),e.lanes=n,e}function Nl(e,t,n,r){return e=Ne(22,e,r,t),e.elementType=Ka,e.lanes=n,e.stateNode={isHidden:!1},e}function ao(e,t,n){return e=Ne(6,e,null,t),e.lanes=n,e}function so(e,t,n){return t=Ne(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function _p(e,t,n,r,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Wl(0),this.expirationTimes=Wl(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Wl(0),this.identifierPrefix=r,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function bi(e,t,n,r,l,o,i,u,a){return e=new _p(e,t,n,u,a),t===1?(t=1,o===!0&&(t|=8)):t=0,o=Ne(3,null,null,t),e.current=o,o.stateNode=e,o.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Mi(o),e}function Cp(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE($c)}catch(e){console.error(e)}}$c(),$a.exports=Ee;var jp=$a.exports,ma=jp;fo.createRoot=ma.createRoot,fo.hydrateRoot=ma.hydrateRoot;/** + * @remix-run/router v1.23.3 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function ir(){return ir=Object.assign?Object.assign.bind():function(e){for(var t=1;t"u")throw new Error(t)}function ru(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function Tp(){return Math.random().toString(36).substr(2,8)}function ya(e,t){return{usr:e.state,key:e.key,idx:t}}function oi(e,t,n,r){return n===void 0&&(n=null),ir({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?gn(t):t,{state:n,key:t&&t.key||r||Tp()})}function hl(e){let{pathname:t="/",search:n="",hash:r=""}=e;return n&&n!=="?"&&(t+=n.charAt(0)==="?"?n:"?"+n),r&&r!=="#"&&(t+=r.charAt(0)==="#"?r:"#"+r),t}function gn(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substr(n),e=e.substr(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substr(r),e=e.substr(0,r)),e&&(t.pathname=e)}return t}function Op(e,t,n,r){r===void 0&&(r={});let{window:l=document.defaultView,v5Compat:o=!1}=r,i=l.history,u=st.Pop,a=null,s=h();s==null&&(s=0,i.replaceState(ir({},i.state,{idx:s}),""));function h(){return(i.state||{idx:null}).idx}function p(){u=st.Pop;let _=h(),f=_==null?null:_-s;s=_,a&&a({action:u,location:k.location,delta:f})}function m(_,f){u=st.Push;let c=oi(k.location,_,f);s=h()+1;let d=ya(c,s),v=k.createHref(c);try{i.pushState(d,"",v)}catch(E){if(E instanceof DOMException&&E.name==="DataCloneError")throw E;l.location.assign(v)}o&&a&&a({action:u,location:k.location,delta:1})}function S(_,f){u=st.Replace;let c=oi(k.location,_,f);s=h();let d=ya(c,s),v=k.createHref(c);i.replaceState(d,"",v),o&&a&&a({action:u,location:k.location,delta:0})}function w(_){let f=l.location.origin!=="null"?l.location.origin:l.location.href,c=typeof _=="string"?_:hl(_);return c=c.replace(/ $/,"%20"),W(f,"No window.location.(origin|href) available to create URL for href: "+c),new URL(c,f)}let k={get action(){return u},get location(){return e(l,i)},listen(_){if(a)throw new Error("A history only accepts one active listener");return l.addEventListener(va,p),a=_,()=>{l.removeEventListener(va,p),a=null}},createHref(_){return t(l,_)},createURL:w,encodeLocation(_){let f=w(_);return{pathname:f.pathname,search:f.search,hash:f.hash}},push:m,replace:S,go(_){return i.go(_)}};return k}var ga;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(ga||(ga={}));function Ip(e,t,n){return n===void 0&&(n="/"),Dp(e,t,n)}function Dp(e,t,n,r){let l=typeof t=="string"?gn(t):t,o=hn(l.pathname||"/",n);if(o==null)return null;let i=Bc(e);Mp(i);let u=null,a=Yp(o);for(let s=0;u==null&&s{let a={relativePath:u===void 0?o.path||"":u,caseSensitive:o.caseSensitive===!0,childrenIndex:i,route:o};a.relativePath.startsWith("/")&&(W(a.relativePath.startsWith(r),'Absolute route path "'+a.relativePath+'" nested under path '+('"'+r+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),a.relativePath=a.relativePath.slice(r.length));let s=gt([r,a.relativePath]),h=n.concat(a);o.children&&o.children.length>0&&(W(o.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+s+'".')),Bc(o.children,t,h,s)),!(o.path==null&&!o.index)&&t.push({path:s,score:Wp(s,o.index),routesMeta:h})};return e.forEach((o,i)=>{var u;if(o.path===""||!((u=o.path)!=null&&u.includes("?")))l(o,i);else for(let a of Vc(o.path))l(o,i,a)}),t}function Vc(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,l=n.endsWith("?"),o=n.replace(/\?$/,"");if(r.length===0)return l?[o,""]:[o];let i=Vc(r.join("/")),u=[];return u.push(...i.map(a=>a===""?o:[o,a].join("/"))),l&&u.push(...i),u.map(a=>e.startsWith("/")&&a===""?"/":a)}function Mp(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:Hp(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}const Fp=/^:[\w-]+$/,Up=3,Ap=2,$p=1,Bp=10,Vp=-2,wa=e=>e==="*";function Wp(e,t){let n=e.split("/"),r=n.length;return n.some(wa)&&(r+=Vp),t&&(r+=Ap),n.filter(l=>!wa(l)).reduce((l,o)=>l+(Fp.test(o)?Up:o===""?$p:Bp),r)}function Hp(e,t){return e.length===t.length&&e.slice(0,-1).every((r,l)=>r===t[l])?e[e.length-1]-t[t.length-1]:0}function Qp(e,t,n){let{routesMeta:r}=e,l={},o="/",i=[];for(let u=0;u{let{paramName:m,isOptional:S}=h;if(m==="*"){let k=u[p]||"";i=o.slice(0,o.length-k.length).replace(/(.)\/+$/,"$1")}const w=u[p];return S&&!w?s[m]=void 0:s[m]=(w||"").replace(/%2F/g,"/"),s},{}),pathname:o,pathnameBase:i,pattern:e}}function Kp(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!0),ru(e==="*"||!e.endsWith("*")||e.endsWith("/*"),'Route path "'+e+'" will be treated as if it were '+('"'+e.replace(/\*$/,"/*")+'" because the `*` character must ')+"always follow a `/` in the pattern. To get rid of this warning, "+('please change the route path to "'+e.replace(/\*$/,"/*")+'".'));let r=[],l="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(i,u,a)=>(r.push({paramName:u,isOptional:a!=null}),a?"/?([^\\/]+)?":"/([^\\/]+)"));return e.endsWith("*")?(r.push({paramName:"*"}),l+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?l+="\\/*$":e!==""&&e!=="/"&&(l+="(?:(?=\\/|$))"),[new RegExp(l,t?void 0:"i"),r]}function Yp(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return ru(!1,'The URL path "'+e+'" could not be decoded because it is is a malformed URL segment. This is probably due to a bad percent '+("encoding ("+t+").")),e}}function hn(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}const Gp=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Xp=e=>Gp.test(e);function Zp(e,t){t===void 0&&(t="/");let{pathname:n,search:r="",hash:l=""}=typeof e=="string"?gn(e):e,o;if(n)if(Xp(n))o=n;else{if(n.includes("//")){let i=n;n=Wc(n),ru(!1,"Pathnames cannot have embedded double slashes - normalizing "+(i+" -> "+n))}n.startsWith("/")?o=Sa(n.substring(1),"/"):o=Sa(n,t)}else o=t;return{pathname:o,search:bp(r),hash:eh(l)}}function Sa(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(l=>{l===".."?n.length>1&&n.pop():l!=="."&&n.push(l)}),n.length>1?n.join("/"):"/"}function co(e,t,n,r){return"Cannot include a '"+e+"' character in a manually specified "+("`to."+t+"` field ["+JSON.stringify(r)+"]. Please separate it out to the ")+("`to."+n+"` field. Alternatively you may provide the full path as ")+'a string in and the router will parse it for you.'}function Jp(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function lu(e,t){let n=Jp(e);return t?n.map((r,l)=>l===n.length-1?r.pathname:r.pathnameBase):n.map(r=>r.pathnameBase)}function ou(e,t,n,r){r===void 0&&(r=!1);let l;typeof e=="string"?l=gn(e):(l=ir({},e),W(!l.pathname||!l.pathname.includes("?"),co("?","pathname","search",l)),W(!l.pathname||!l.pathname.includes("#"),co("#","pathname","hash",l)),W(!l.search||!l.search.includes("#"),co("#","search","hash",l)));let o=e===""||l.pathname==="",i=o?"/":l.pathname,u;if(i==null)u=n;else{let p=t.length-1;if(!r&&i.startsWith("..")){let m=i.split("/");for(;m[0]==="..";)m.shift(),p-=1;l.pathname=m.join("/")}u=p>=0?t[p]:"/"}let a=Zp(l,u),s=i&&i!=="/"&&i.endsWith("/"),h=(o||i===".")&&n.endsWith("/");return!a.pathname.endsWith("/")&&(s||h)&&(a.pathname+="/"),a}const Wc=e=>e.replace(/\/\/+/g,"/"),gt=e=>Wc(e.join("/")),qp=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),bp=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,eh=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;function th(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}const Hc=["post","put","patch","delete"];new Set(Hc);const nh=["get",...Hc];new Set(nh);/** + * React Router v6.30.4 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function ur(){return ur=Object.assign?Object.assign.bind():function(e){for(var t=1;t{u.current=!0}),g.useCallback(function(s,h){if(h===void 0&&(h={}),!u.current)return;if(typeof s=="number"){r.go(s);return}let p=ou(s,JSON.parse(i),o,h.relative==="path");e==null&&t!=="/"&&(p.pathname=p.pathname==="/"?t:gt([t,p.pathname])),(h.replace?r.replace:r.push)(p,h.state,h)},[t,r,i,o,e])}const oh=g.createContext(null);function ih(e){let t=g.useContext(tt).outlet;return t&&g.createElement(oh.Provider,{value:e},t)}function Il(e,t){let{relative:n}=t===void 0?{}:t,{future:r}=g.useContext(et),{matches:l}=g.useContext(tt),{pathname:o}=_t(),i=JSON.stringify(lu(l,r.v7_relativeSplatPath));return g.useMemo(()=>ou(e,JSON.parse(i),o,n==="path"),[e,i,o,n])}function uh(e,t){return ah(e,t)}function ah(e,t,n,r){wn()||W(!1);let{navigator:l}=g.useContext(et),{matches:o}=g.useContext(tt),i=o[o.length-1],u=i?i.params:{};i&&i.pathname;let a=i?i.pathnameBase:"/";i&&i.route;let s=_t(),h;if(t){var p;let _=typeof t=="string"?gn(t):t;a==="/"||(p=_.pathname)!=null&&p.startsWith(a)||W(!1),h=_}else h=s;let m=h.pathname||"/",S=m;if(a!=="/"){let _=a.replace(/^\//,"").split("/");S="/"+m.replace(/^\//,"").split("/").slice(_.length).join("/")}let w=Ip(e,{pathname:S}),k=ph(w&&w.map(_=>Object.assign({},_,{params:Object.assign({},u,_.params),pathname:gt([a,l.encodeLocation?l.encodeLocation(_.pathname).pathname:_.pathname]),pathnameBase:_.pathnameBase==="/"?a:gt([a,l.encodeLocation?l.encodeLocation(_.pathnameBase).pathname:_.pathnameBase])})),o,n,r);return t&&k?g.createElement(Ol.Provider,{value:{location:ur({pathname:"/",search:"",hash:"",state:null,key:"default"},h),navigationType:st.Pop}},k):k}function sh(){let e=yh(),t=th(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,l={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"};return g.createElement(g.Fragment,null,g.createElement("h2",null,"Unexpected Application Error!"),g.createElement("h3",{style:{fontStyle:"italic"}},t),n?g.createElement("pre",{style:l},n):null,null)}const ch=g.createElement(sh,null);class fh extends g.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,n){return n.location!==t.location||n.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:n.error,location:n.location,revalidation:t.revalidation||n.revalidation}}componentDidCatch(t,n){console.error("React Router caught the following error during render",t,n)}render(){return this.state.error!==void 0?g.createElement(tt.Provider,{value:this.props.routeContext},g.createElement(Kc.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function dh(e){let{routeContext:t,match:n,children:r}=e,l=g.useContext(Tl);return l&&l.static&&l.staticContext&&(n.route.errorElement||n.route.ErrorBoundary)&&(l.staticContext._deepestRenderedBoundaryId=n.route.id),g.createElement(tt.Provider,{value:t},r)}function ph(e,t,n,r){var l;if(t===void 0&&(t=[]),n===void 0&&(n=null),r===void 0&&(r=null),e==null){var o;if(!n)return null;if(n.errors)e=n.matches;else if((o=r)!=null&&o.v7_partialHydration&&t.length===0&&!n.initialized&&n.matches.length>0)e=n.matches;else return null}let i=e,u=(l=n)==null?void 0:l.errors;if(u!=null){let h=i.findIndex(p=>p.route.id&&(u==null?void 0:u[p.route.id])!==void 0);h>=0||W(!1),i=i.slice(0,Math.min(i.length,h+1))}let a=!1,s=-1;if(n&&r&&r.v7_partialHydration)for(let h=0;h=0?i=i.slice(0,s+1):i=[i[0]];break}}}return i.reduceRight((h,p,m)=>{let S,w=!1,k=null,_=null;n&&(S=u&&p.route.id?u[p.route.id]:void 0,k=p.route.errorElement||ch,a&&(s<0&&m===0?(wh("route-fallback"),w=!0,_=null):s===m&&(w=!0,_=p.route.hydrateFallbackElement||null)));let f=t.concat(i.slice(0,m+1)),c=()=>{let d;return S?d=k:w?d=_:p.route.Component?d=g.createElement(p.route.Component,null):p.route.element?d=p.route.element:d=h,g.createElement(dh,{match:p,routeContext:{outlet:h,matches:f,isDataRoute:n!=null},children:d})};return n&&(p.route.ErrorBoundary||p.route.errorElement||m===0)?g.createElement(fh,{location:n.location,revalidation:n.revalidation,component:k,error:S,children:c(),routeContext:{outlet:null,matches:f,isDataRoute:!0}}):c()},null)}var Xc=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e}(Xc||{}),Zc=function(e){return e.UseBlocker="useBlocker",e.UseLoaderData="useLoaderData",e.UseActionData="useActionData",e.UseRouteError="useRouteError",e.UseNavigation="useNavigation",e.UseRouteLoaderData="useRouteLoaderData",e.UseMatches="useMatches",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e.UseRouteId="useRouteId",e}(Zc||{});function hh(e){let t=g.useContext(Tl);return t||W(!1),t}function mh(e){let t=g.useContext(Qc);return t||W(!1),t}function vh(e){let t=g.useContext(tt);return t||W(!1),t}function Jc(e){let t=vh(),n=t.matches[t.matches.length-1];return n.route.id||W(!1),n.route.id}function yh(){var e;let t=g.useContext(Kc),n=mh(),r=Jc();return t!==void 0?t:(e=n.errors)==null?void 0:e[r]}function gh(){let{router:e}=hh(Xc.UseNavigateStable),t=Jc(Zc.UseNavigateStable),n=g.useRef(!1);return Yc(()=>{n.current=!0}),g.useCallback(function(l,o){o===void 0&&(o={}),n.current&&(typeof l=="number"?e.navigate(l):e.navigate(l,ur({fromRouteId:t},o)))},[e,t])}const ka={};function wh(e,t,n){ka[e]||(ka[e]=!0)}function Sh(e,t){e==null||e.v7_startTransition,e==null||e.v7_relativeSplatPath}function xa(e){let{to:t,replace:n,state:r,relative:l}=e;wn()||W(!1);let{future:o,static:i}=g.useContext(et),{matches:u}=g.useContext(tt),{pathname:a}=_t(),s=Gc(),h=ou(t,lu(u,o.v7_relativeSplatPath),a,l==="path"),p=JSON.stringify(h);return g.useEffect(()=>s(JSON.parse(p),{replace:n,state:r,relative:l}),[s,p,l,n,r]),null}function om(e){return ih(e.context)}function G(e){W(!1)}function kh(e){let{basename:t="/",children:n=null,location:r,navigationType:l=st.Pop,navigator:o,static:i=!1,future:u}=e;wn()&&W(!1);let a=t.replace(/^\/*/,"/"),s=g.useMemo(()=>({basename:a,navigator:o,static:i,future:ur({v7_relativeSplatPath:!1},u)}),[a,u,o,i]);typeof r=="string"&&(r=gn(r));let{pathname:h="/",search:p="",hash:m="",state:S=null,key:w="default"}=r,k=g.useMemo(()=>{let _=hn(h,a);return _==null?null:{location:{pathname:_,search:p,hash:m,state:S,key:w},navigationType:l}},[a,h,p,m,S,w,l]);return k==null?null:g.createElement(et.Provider,{value:s},g.createElement(Ol.Provider,{children:n,value:k}))}function Ea(e){let{children:t,location:n}=e;return uh(ui(t),n)}new Promise(()=>{});function ui(e,t){t===void 0&&(t=[]);let n=[];return g.Children.forEach(e,(r,l)=>{if(!g.isValidElement(r))return;let o=[...t,l];if(r.type===g.Fragment){n.push.apply(n,ui(r.props.children,o));return}r.type!==G&&W(!1),!r.props.index||!r.props.children||W(!1);let i={id:r.props.id||o.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,loader:r.props.loader,action:r.props.action,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(i.children=ui(r.props.children,o)),n.push(i)}),n}/** + * React Router DOM v6.30.4 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function ml(){return ml=Object.assign?Object.assign.bind():function(e){for(var t=1;t{s&&_a?_a(()=>a(p)):a(p)},[a,s]);return g.useLayoutEffect(()=>i.listen(h),[i,h]),g.useEffect(()=>Sh(r),[r]),g.createElement(kh,{basename:t,children:n,location:u.location,navigationType:u.action,navigator:i,future:r})}const jh=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",zh=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,De=g.forwardRef(function(t,n){let{onClick:r,relative:l,reloadDocument:o,replace:i,state:u,target:a,to:s,preventScrollReset:h,viewTransition:p}=t,m=qc(t,_h),{basename:S}=g.useContext(et),w,k=!1;if(typeof s=="string"&&zh.test(s)&&(w=s,jh))try{let d=new URL(window.location.href),v=s.startsWith("//")?new URL(d.protocol+s):new URL(s),E=hn(v.pathname,S);v.origin===d.origin&&E!=null?s=E+v.search+v.hash:k=!0}catch{}let _=rh(s,{relative:l}),f=Oh(s,{replace:i,state:u,target:a,preventScrollReset:h,relative:l,viewTransition:p});function c(d){r&&r(d),d.defaultPrevented||f(d)}return g.createElement("a",ml({},m,{href:w||_,onClick:k||o?r:c,ref:n,target:a}))}),im=g.forwardRef(function(t,n){let{"aria-current":r="page",caseSensitive:l=!1,className:o="",end:i=!1,style:u,to:a,viewTransition:s,children:h}=t,p=qc(t,Ch),m=Il(a,{relative:p.relative}),S=_t(),w=g.useContext(Qc),{navigator:k,basename:_}=g.useContext(et),f=w!=null&&Ih(m)&&s===!0,c=k.encodeLocation?k.encodeLocation(m).pathname:m.pathname,d=S.pathname,v=w&&w.navigation&&w.navigation.location?w.navigation.location.pathname:null;l||(d=d.toLowerCase(),v=v?v.toLowerCase():null,c=c.toLowerCase()),v&&_&&(v=hn(v,_)||v);const E=c!=="/"&&c.endsWith("/")?c.length-1:c.length;let N=d===c||!i&&d.startsWith(c)&&d.charAt(E)==="/",L=v!=null&&(v===c||!i&&v.startsWith(c)&&v.charAt(c.length)==="/"),R={isActive:N,isPending:L,isTransitioning:f},$=N?r:void 0,z;typeof o=="function"?z=o(R):z=[o,N?"active":null,L?"pending":null,f?"transitioning":null].filter(Boolean).join(" ");let ge=typeof u=="function"?u(R):u;return g.createElement(De,ml({},p,{"aria-current":$,className:z,ref:n,style:ge,to:a,viewTransition:s}),typeof h=="function"?h(R):h)});var ai;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(ai||(ai={}));var Ca;(function(e){e.UseFetcher="useFetcher",e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(Ca||(Ca={}));function Th(e){let t=g.useContext(Tl);return t||W(!1),t}function Oh(e,t){let{target:n,replace:r,state:l,preventScrollReset:o,relative:i,viewTransition:u}=t===void 0?{}:t,a=Gc(),s=_t(),h=Il(e,{relative:i});return g.useCallback(p=>{if(Eh(p,n)){p.preventDefault();let m=r!==void 0?r:hl(s)===hl(h);a(e,{replace:m,state:l,preventScrollReset:o,relative:i,viewTransition:u})}},[s,a,h,r,l,n,e,o,i,u])}function Ih(e,t){t===void 0&&(t={});let n=g.useContext(Nh);n==null&&W(!1);let{basename:r}=Th(ai.useViewTransitionState),l=Il(e,{relative:t.relative});if(!n.isTransitioning)return!1;let o=hn(n.currentLocation.pathname,r)||n.currentLocation.pathname,i=hn(n.nextLocation.pathname,r)||n.nextLocation.pathname;return ii(l.pathname,i)!=null||ii(l.pathname,o)!=null}const Dh="modulepreload",Mh=function(e){return"/"+e},Pa={},le=function(t,n,r){let l=Promise.resolve();if(n&&n.length>0){document.getElementsByTagName("link");const i=document.querySelector("meta[property=csp-nonce]"),u=(i==null?void 0:i.nonce)||(i==null?void 0:i.getAttribute("nonce"));l=Promise.allSettled(n.map(a=>{if(a=Mh(a),a in Pa)return;Pa[a]=!0;const s=a.endsWith(".css"),h=s?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${a}"]${h}`))return;const p=document.createElement("link");if(p.rel=s?"stylesheet":Dh,s||(p.as="script"),p.crossOrigin="",p.href=a,u&&p.setAttribute("nonce",u),document.head.appendChild(p),s)return new Promise((m,S)=>{p.addEventListener("load",m),p.addEventListener("error",()=>S(new Error(`Unable to preload CSS for ${a}`)))})}))}function o(i){const u=new Event("vite:preloadError",{cancelable:!0});if(u.payload=i,window.dispatchEvent(u),!u.defaultPrevented)throw i}return l.then(i=>{for(const u of i||[])u.status==="rejected"&&o(u.reason);return t().catch(o)})},Na=[{id:"company",label:"ํšŒ์‚ฌ์†Œ๊ฐœ",children:[{label:"CEO ์ธ์‚ฌ๋ง",path:"/company/greeting"},{label:"์—ฐํ˜",path:"/company/history"},{label:"์กฐ์ง๋„",path:"/company/organization"},{label:"CI ์†Œ๊ฐœ",path:"/company/ci"},{label:"์˜ค์‹œ๋Š” ๊ธธ",path:"/company/location"}]},{id:"solution",label:"์†”๋ฃจ์…˜",children:[{label:"GUARDiA ITSM",path:"/solution/guardia",badge:"NEW"},{label:"ERP",path:"/solution/erp"},{label:"CRM",path:"/solution/crm"},{label:"BI",path:"/solution/bi"}]},{id:"business",label:"์‚ฌ์—…์‹ค์ ",children:[{label:"๊ตฌ์ถ• ๋ ˆํผ๋Ÿฐ์Šค",path:"/business/reference"},{label:"ํŒŒํŠธ๋„ˆ",path:"/business/partner"}]},{id:"support",label:"๊ณ ๊ฐ์ง€์›",children:[{label:"๊ณต์ง€์‚ฌํ•ญ",path:"/support/notice"},{label:"FAQ",path:"/support/faq"},{label:"์นดํƒˆ๋กœ๊ทธ",path:"/support/catalog"},{label:"๋ฌธ์˜ํ•˜๊ธฐ",path:"/support/contact"}]},{id:"recruit",label:"์ฑ„์šฉ",children:[{label:"์ฑ„์šฉ๊ณต๊ณ ",path:"/recruit/jobs"},{label:"๋ณต๋ฆฌํ›„์ƒ",path:"/recruit/welfare"},{label:"์ง€์›ํ•˜๊ธฐ",path:"/recruit/apply"}]},{id:"news",label:"๋‰ด์Šค",children:[{label:"๋‰ด์Šค๋ฃธ",path:"/news/newsroom"},{label:"๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ",path:"/news/blog"}]}];function Fh(){const[e,t]=g.useState(!1),[n,r]=g.useState(null),[l,o]=g.useState(!1),i=_t();g.useEffect(()=>{const a=()=>t(window.scrollY>60);return window.addEventListener("scroll",a,{passive:!0}),()=>window.removeEventListener("scroll",a)},[]),g.useEffect(()=>{o(!1),r(null)},[i]);const u=a=>{var s;return(s=a.children)==null?void 0:s.some(h=>i.pathname.startsWith(h.path))};return y.jsxs(y.Fragment,{children:[y.jsx("a",{href:"#main-content",className:"skip-link",children:"๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ"}),y.jsxs("header",{className:`header ${e?"scrolled":""} ${l?"mobile-open":""}`,role:"banner",children:[y.jsxs("div",{className:"header-inner container",children:[y.jsxs(De,{to:"/",className:"logo","aria-label":"(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ํ™ˆ์œผ๋กœ",children:[y.jsx("img",{src:"/logo.png",alt:"(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ๋กœ๊ณ ",height:"40",onError:a=>{a.target.style.display="none",a.target.nextSibling.style.display="flex"}}),y.jsxs("span",{className:"logo-text",style:{display:"none"},children:[y.jsx("strong",{children:"Zio"}),"Info"]})]}),y.jsx("nav",{className:"nav-desktop",role:"navigation","aria-label":"์ฃผ์š” ๋ฉ”๋‰ด",children:Na.map(a=>y.jsxs("div",{className:`nav-item ${u(a)?"active":""}`,onMouseEnter:()=>r(a.id),onMouseLeave:()=>r(null),children:[y.jsx("button",{className:"nav-trigger","aria-haspopup":"true","aria-expanded":n===a.id,children:a.label}),n===a.id&&y.jsx("div",{className:"dropdown",role:"menu",children:a.children.map(s=>y.jsxs(De,{to:s.path,className:`dropdown-item ${i.pathname===s.path?"current":""}`,role:"menuitem",children:[s.label,s.badge&&y.jsx("span",{className:"badge badge-new",children:s.badge})]},s.path))})]},a.id))}),y.jsx(De,{to:"/support/contact",className:"btn btn-primary btn-sm header-cta",children:"๋ฌธ์˜ํ•˜๊ธฐ"}),y.jsxs("button",{className:"hamburger","aria-label":"๋ชจ๋ฐ”์ผ ๋ฉ”๋‰ด","aria-expanded":l,onClick:()=>o(a=>!a),children:[y.jsx("span",{}),y.jsx("span",{}),y.jsx("span",{})]})]}),l&&y.jsxs("nav",{className:"nav-mobile",role:"navigation","aria-label":"๋ชจ๋ฐ”์ผ ๋ฉ”๋‰ด",children:[Na.map(a=>y.jsxs("details",{className:"mobile-group",children:[y.jsx("summary",{className:"mobile-group-header",children:a.label}),y.jsx("div",{className:"mobile-children",children:a.children.map(s=>y.jsxs(De,{to:s.path,className:"mobile-child",children:[s.label,s.badge&&y.jsx("span",{className:"badge badge-new",children:s.badge})]},s.path))})]},a.id)),y.jsx(De,{to:"/support/contact",className:"btn btn-primary",style:{margin:"16px"},children:"๋ฌธ์˜ํ•˜๊ธฐ"})]})]})]})}const Uh=[{title:"ํšŒ์‚ฌ์†Œ๊ฐœ",links:[{label:"CEO ์ธ์‚ฌ๋ง",path:"/company/greeting"},{label:"์—ฐํ˜",path:"/company/history"},{label:"์กฐ์ง๋„",path:"/company/organization"},{label:"์˜ค์‹œ๋Š” ๊ธธ",path:"/company/location"}]},{title:"์†”๋ฃจ์…˜",links:[{label:"GUARDiA ITSM",path:"/solution/guardia"},{label:"ERP",path:"/solution/erp"},{label:"CRM",path:"/solution/crm"},{label:"BI",path:"/solution/bi"}]},{title:"๊ณ ๊ฐ์ง€์›",links:[{label:"๊ณต์ง€์‚ฌํ•ญ",path:"/support/notice"},{label:"FAQ",path:"/support/faq"},{label:"์นดํƒˆ๋กœ๊ทธ",path:"/support/catalog"},{label:"๋ฌธ์˜ํ•˜๊ธฐ",path:"/support/contact"}]},{title:"์ฑ„์šฉ",links:[{label:"์ฑ„์šฉ๊ณต๊ณ ",path:"/recruit/jobs"},{label:"๋ณต๋ฆฌํ›„์ƒ",path:"/recruit/welfare"},{label:"์ง€์›ํ•˜๊ธฐ",path:"/recruit/apply"}]}];function Ah(){return y.jsxs("footer",{className:"footer",role:"contentinfo",children:[y.jsx("div",{className:"footer-top",children:y.jsxs("div",{className:"container footer-top-inner",children:[y.jsxs("div",{className:"footer-brand",children:[y.jsxs(De,{to:"/",className:"footer-logo",children:[y.jsx("img",{src:"/logo-white.png",alt:"(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ๋กœ๊ณ ",height:"36",onError:e=>{e.target.style.display="none",e.target.nextSibling.style.display="block"}}),y.jsxs("span",{className:"footer-logo-text",style:{display:"none"},children:[y.jsx("strong",{children:"Zio"}),"Info"]})]}),y.jsxs("p",{className:"footer-tagline",children:["AI ๊ธฐ๋ฐ˜ ๋ ˆ๊ฑฐ์‹œ ์ธํ”„๋ผ ์ž์œจ ์šด์˜ ํ”Œ๋žซํผ",y.jsx("br",{}),"GUARDiA ITSM์œผ๋กœ ๊ณต๊ณต๊ธฐ๊ด€ IT๋ฅผ ํ˜์‹ ํ•ฉ๋‹ˆ๋‹ค."]}),y.jsxs("div",{className:"footer-contact-list",children:[y.jsxs("div",{className:"footer-contact-item",children:[y.jsx("span",{className:"contact-label",children:"๋Œ€ํ‘œ์ „ํ™”"}),y.jsx("span",{children:"02-000-0000"})]}),y.jsxs("div",{className:"footer-contact-item",children:[y.jsx("span",{className:"contact-label",children:"์ด๋ฉ”์ผ"}),y.jsx("a",{href:"mailto:info@zioinfo.co.kr",children:"info@zioinfo.co.kr"})]}),y.jsxs("div",{className:"footer-contact-item",children:[y.jsx("span",{className:"contact-label",children:"์ฃผ์†Œ"}),y.jsx("span",{children:"์„œ์šธํŠน๋ณ„์‹œ"})]})]})]}),Uh.map((e,t)=>y.jsxs("div",{className:"footer-menu-group",children:[y.jsx("h3",{className:"footer-menu-title",children:e.title}),y.jsx("ul",{className:"footer-menu-list",children:e.links.map((n,r)=>y.jsx("li",{children:y.jsx(De,{to:n.path,children:n.label})},r))})]},t))]})}),y.jsx("div",{className:"footer-bottom",children:y.jsxs("div",{className:"container footer-bottom-inner",children:[y.jsxs("div",{className:"footer-legal",children:[y.jsx(De,{to:"/privacy",children:"๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจ"}),y.jsx(De,{to:"/terms",children:"์ด์šฉ์•ฝ๊ด€"}),y.jsx(De,{to:"/sitemap",children:"์‚ฌ์ดํŠธ๋งต"})]}),y.jsx("p",{className:"footer-copyright",children:"Copyright ยฉ 2026 (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  All Rights Reserved."}),y.jsxs("div",{className:"footer-powered",children:["Powered by ",y.jsx("strong",{children:"GUARDiA ITSM"})]})]})})]})}const $h=g.lazy(()=>le(()=>import("./Home-BC38QtTl.js"),__vite__mapDeps([0,1,2]))),Bh=g.lazy(()=>le(()=>import("./GuardiaDetail-5Pm8bk4O.js"),__vite__mapDeps([3,4]))),Vh=g.lazy(()=>le(()=>import("./SolutionPage-Da0Vpoc-.js"),__vite__mapDeps([5,6,7]))),Wh=g.lazy(()=>le(()=>import("./Company-BOdWAIQ4.js"),__vite__mapDeps([8,9,7]))),Hh=g.lazy(()=>le(()=>import("./Business-EGnXphuY.js"),__vite__mapDeps([10,11,7]))),Qh=g.lazy(()=>le(()=>import("./Contact-C6p_tBWi.js"),__vite__mapDeps([12,1,13]))),Kh=g.lazy(()=>le(()=>import("./Support-C5QVP1gW.js"),__vite__mapDeps([14,15,7]))),Yh=g.lazy(()=>le(()=>import("./NewsPage-mgytOZhS.js"),__vite__mapDeps([16,17,7]))),Gh=g.lazy(()=>le(()=>import("./Recruit-DlKGM6KQ.js"),__vite__mapDeps([18,19,7]))),Xh=g.lazy(()=>le(()=>import("./NotFound-KZZDVQMb.js"),[])),Zh=g.lazy(()=>le(()=>import("./AdminLogin-DcRT5LbX.js"),__vite__mapDeps([20,21]))),Jh=g.lazy(()=>le(()=>import("./AdminLayout-uWX9KsdF.js"),__vite__mapDeps([22,21]))),qh=g.lazy(()=>le(()=>import("./AdminDashboard-B5ryl_KI.js"),[])),bh=g.lazy(()=>le(()=>import("./AdminNews-CDSgPR9E.js"),[])),em=g.lazy(()=>le(()=>import("./AdminInquiry-BBFBdE8S.js"),[])),tm=g.lazy(()=>le(()=>import("./AdminRecruit-CfX4mhQb.js"),[])),nm=g.lazy(()=>le(()=>import("./AdminSettings-DaHEGHsg.js"),[]));function bc(){return y.jsx("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",height:"60vh",color:"var(--gray-400)",fontSize:"14px"},children:"๋กœ๋”ฉ ์ค‘..."})}function rm({children:e}){return y.jsxs(y.Fragment,{children:[y.jsx(Fh,{}),y.jsx(g.Suspense,{fallback:y.jsx(bc,{}),children:e}),y.jsx(Ah,{})]})}function lm(){return _t().pathname.startsWith("/admin")?y.jsx(g.Suspense,{fallback:y.jsx(bc,{}),children:y.jsxs(Ea,{children:[y.jsx(G,{path:"/admin/login",element:y.jsx(Zh,{})}),y.jsxs(G,{path:"/admin",element:y.jsx(Jh,{}),children:[y.jsx(G,{index:!0,element:y.jsx(xa,{to:"/admin/dashboard",replace:!0})}),y.jsx(G,{path:"dashboard",element:y.jsx(qh,{})}),y.jsx(G,{path:"news",element:y.jsx(bh,{})}),y.jsx(G,{path:"inquiries",element:y.jsx(em,{})}),y.jsx(G,{path:"recruit",element:y.jsx(tm,{})}),y.jsx(G,{path:"settings",element:y.jsx(nm,{})})]}),y.jsx(G,{path:"*",element:y.jsx(xa,{to:"/admin/login",replace:!0})})]})}):y.jsx(rm,{children:y.jsxs(Ea,{children:[y.jsx(G,{path:"/",element:y.jsx($h,{})}),y.jsx(G,{path:"/solution/guardia",element:y.jsx(Bh,{})}),y.jsx(G,{path:"/solution/*",element:y.jsx(Vh,{})}),y.jsx(G,{path:"/company/*",element:y.jsx(Wh,{})}),y.jsx(G,{path:"/business/*",element:y.jsx(Hh,{})}),y.jsx(G,{path:"/support/contact",element:y.jsx(Qh,{})}),y.jsx(G,{path:"/support/*",element:y.jsx(Kh,{})}),y.jsx(G,{path:"/recruit/*",element:y.jsx(Gh,{})}),y.jsx(G,{path:"/news/*",element:y.jsx(Yh,{})}),y.jsx(G,{path:"*",element:y.jsx(Xh,{})})]})})}fo.createRoot(document.getElementById("root")).render(y.jsx(Ua.StrictMode,{children:y.jsx(Rh,{children:y.jsx(lm,{})})}));export{De as L,im as N,om as O,Ua as R,G as a,Ea as b,Gc as c,y as j,g as r,_t as u}; diff --git a/backend/src/main/resources/static/assets/index-DcNlVx-A.js b/backend/src/main/resources/static/assets/index-DcNlVx-A.js new file mode 100644 index 0000000..9cf4beb --- /dev/null +++ b/backend/src/main/resources/static/assets/index-DcNlVx-A.js @@ -0,0 +1,9 @@ +function tt(e,t){return function(){return e.apply(t,arguments)}}const{toString:_t}=Object.prototype,{getPrototypeOf:de}=Object,{iterator:pe,toStringTag:nt}=Symbol,he=(e=>t=>{const n=_t.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null)),L=e=>(e=e.toLowerCase(),t=>he(t)===e),me=e=>t=>typeof t===e,{isArray:v}=Array,K=me("undefined");function Z(e){return e!==null&&!K(e)&&e.constructor!==null&&!K(e.constructor)&&C(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const rt=L("ArrayBuffer");function Tt(e){let t;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?t=ArrayBuffer.isView(e):t=e&&e.buffer&&rt(e.buffer),t}const xt=me("string"),C=me("function"),st=me("number"),ee=e=>e!==null&&typeof e=="object",Ct=e=>e===!0||e===!1,ce=e=>{if(he(e)!=="object")return!1;const t=de(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(nt in e)&&!(pe in e)},Nt=e=>{if(!ee(e)||Z(e))return!1;try{return Object.keys(e).length===0&&Object.getPrototypeOf(e)===Object.prototype}catch{return!1}},Pt=L("Date"),Dt=L("File"),Lt=e=>!!(e&&typeof e.uri<"u"),Ft=e=>e&&typeof e.getParts<"u",Ut=L("Blob"),Bt=L("FileList"),kt=e=>ee(e)&&C(e.pipe);function jt(){return typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{}}const Me=jt(),ze=typeof Me.FormData<"u"?Me.FormData:void 0,qt=e=>{if(!e)return!1;if(ze&&e instanceof ze)return!0;const t=de(e);if(!t||t===Object.prototype||!C(e.append))return!1;const n=he(e);return n==="formdata"||n==="object"&&C(e.toString)&&e.toString()==="[object FormData]"},It=L("URLSearchParams"),[Ht,Mt,zt,$t]=["ReadableStream","Request","Response","Headers"].map(L),Vt=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function te(e,t,{allOwnKeys:n=!1}={}){if(e===null||typeof e>"u")return;let r,s;if(typeof e!="object"&&(e=[e]),v(e))for(r=0,s=e.length;r0;)if(s=n[r],t===s.toLowerCase())return s;return null}const z=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global,it=e=>!K(e)&&e!==z;function _e(...e){const{caseless:t,skipUndefined:n}=it(this)&&this||{},r={},s=(o,i)=>{if(i==="__proto__"||i==="constructor"||i==="prototype")return;const c=t&&ot(r,i)||i,l=Te(r,c)?r[c]:void 0;ce(l)&&ce(o)?r[c]=_e(l,o):ce(o)?r[c]=_e({},o):v(o)?r[c]=o.slice():(!n||!K(o))&&(r[c]=o)};for(let o=0,i=e.length;o(te(t,(s,o)=>{n&&C(s)?Object.defineProperty(e,o,{__proto__:null,value:tt(s,n),writable:!0,enumerable:!0,configurable:!0}):Object.defineProperty(e,o,{__proto__:null,value:s,writable:!0,enumerable:!0,configurable:!0})},{allOwnKeys:r}),e),Wt=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),Kt=(e,t,n,r)=>{e.prototype=Object.create(t.prototype,r),Object.defineProperty(e.prototype,"constructor",{__proto__:null,value:e,writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(e,"super",{__proto__:null,value:t.prototype}),n&&Object.assign(e.prototype,n)},vt=(e,t,n,r)=>{let s,o,i;const c={};if(t=t||{},e==null)return t;do{for(s=Object.getOwnPropertyNames(e),o=s.length;o-- >0;)i=s[o],(!r||r(i,e,t))&&!c[i]&&(t[i]=e[i],c[i]=!0);e=n!==!1&&de(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},Xt=(e,t,n)=>{e=String(e),(n===void 0||n>e.length)&&(n=e.length),n-=t.length;const r=e.indexOf(t,n);return r!==-1&&r===n},Gt=e=>{if(!e)return null;if(v(e))return e;let t=e.length;if(!st(t))return null;const n=new Array(t);for(;t-- >0;)n[t]=e[t];return n},Qt=(e=>t=>e&&t instanceof e)(typeof Uint8Array<"u"&&de(Uint8Array)),Yt=(e,t)=>{const r=(e&&e[pe]).call(e);let s;for(;(s=r.next())&&!s.done;){const o=s.value;t.call(e,o[0],o[1])}},Zt=(e,t)=>{let n;const r=[];for(;(n=e.exec(t))!==null;)r.push(n);return r},en=L("HTMLFormElement"),tn=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(n,r,s){return r.toUpperCase()+s}),Te=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),nn=L("RegExp"),at=(e,t)=>{const n=Object.getOwnPropertyDescriptors(e),r={};te(n,(s,o)=>{let i;(i=t(s,o,e))!==!1&&(r[o]=i||s)}),Object.defineProperties(e,r)},rn=e=>{at(e,(t,n)=>{if(C(e)&&["arguments","caller","callee"].includes(n))return!1;const r=e[n];if(C(r)){if(t.enumerable=!1,"writable"in t){t.writable=!1;return}t.set||(t.set=()=>{throw Error("Can not rewrite read-only method '"+n+"'")})}})},sn=(e,t)=>{const n={},r=s=>{s.forEach(o=>{n[o]=!0})};return v(e)?r(e):r(String(e).split(t)),n},on=()=>{},an=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t;function cn(e){return!!(e&&C(e.append)&&e[nt]==="FormData"&&e[pe])}const ln=e=>{const t=new WeakSet,n=r=>{if(ee(r)){if(t.has(r))return;if(Z(r))return r;if(!("toJSON"in r)){t.add(r);const s=v(r)?[]:{};return te(r,(o,i)=>{const c=n(o);!K(c)&&(s[i]=c)}),t.delete(r),s}}return r};return n(e)},un=L("AsyncFunction"),fn=e=>e&&(ee(e)||C(e))&&C(e.then)&&C(e.catch),ct=((e,t)=>e?setImmediate:t?((n,r)=>(z.addEventListener("message",({source:s,data:o})=>{s===z&&o===n&&r.length&&r.shift()()},!1),s=>{r.push(s),z.postMessage(n,"*")}))(`axios@${Math.random()}`,[]):n=>setTimeout(n))(typeof setImmediate=="function",C(z.postMessage)),dn=typeof queueMicrotask<"u"?queueMicrotask.bind(z):typeof process<"u"&&process.nextTick||ct,pn=e=>e!=null&&C(e[pe]),a={isArray:v,isArrayBuffer:rt,isBuffer:Z,isFormData:qt,isArrayBufferView:Tt,isString:xt,isNumber:st,isBoolean:Ct,isObject:ee,isPlainObject:ce,isEmptyObject:Nt,isReadableStream:Ht,isRequest:Mt,isResponse:zt,isHeaders:$t,isUndefined:K,isDate:Pt,isFile:Dt,isReactNativeBlob:Lt,isReactNative:Ft,isBlob:Ut,isRegExp:nn,isFunction:C,isStream:kt,isURLSearchParams:It,isTypedArray:Qt,isFileList:Bt,forEach:te,merge:_e,extend:Jt,trim:Vt,stripBOM:Wt,inherits:Kt,toFlatObject:vt,kindOf:he,kindOfTest:L,endsWith:Xt,toArray:Gt,forEachEntry:Yt,matchAll:Zt,isHTMLForm:en,hasOwnProperty:Te,hasOwnProp:Te,reduceDescriptors:at,freezeMethods:rn,toObjectSet:sn,toCamelCase:tn,noop:on,toFiniteNumber:an,findKey:ot,global:z,isContextDefined:it,isSpecCompliantForm:cn,toJSONObject:ln,isAsyncFn:un,isThenable:fn,setImmediate:ct,asap:dn,isIterable:pn},hn=a.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),mn=e=>{const t={};let n,r,s;return e&&e.split(` +`).forEach(function(i){s=i.indexOf(":"),n=i.substring(0,s).trim().toLowerCase(),r=i.substring(s+1).trim(),!(!n||t[n]&&hn[n])&&(n==="set-cookie"?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+", "+r:r)}),t};function yn(e){let t=0,n=e.length;for(;tt;){const r=e.charCodeAt(n-1);if(r!==9&&r!==32)break;n-=1}return t===0&&n===e.length?e:e.slice(t,n)}const bn=new RegExp("[\\u0000-\\u0008\\u000a-\\u001f\\u007f]+","g"),wn=new RegExp("[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+","g");function Pe(e,t){return a.isArray(e)?e.map(n=>Pe(n,t)):yn(String(e).replace(t,""))}const En=e=>Pe(e,bn),Rn=e=>Pe(e,wn);function lt(e){const t=Object.create(null);return a.forEach(e.toJSON(),(n,r)=>{t[r]=Rn(n)}),t}const $e=Symbol("internals");function Y(e){return e&&String(e).trim().toLowerCase()}function le(e){return e===!1||e==null?e:a.isArray(e)?e.map(le):En(String(e))}function gn(e){const t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}const On=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function ge(e,t,n,r,s){if(a.isFunction(r))return r.call(this,t,n);if(s&&(t=n),!!a.isString(t)){if(a.isString(r))return t.indexOf(r)!==-1;if(a.isRegExp(r))return r.test(t)}}function Sn(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(t,n,r)=>n.toUpperCase()+r)}function An(e,t){const n=a.toCamelCase(" "+t);["get","set","has"].forEach(r=>{Object.defineProperty(e,r+n,{__proto__:null,value:function(s,o,i){return this[r].call(this,t,s,o,i)},configurable:!0})})}let x=class{constructor(t){t&&this.set(t)}set(t,n,r){const s=this;function o(c,l,f){const u=Y(l);if(!u)throw new Error("header name must be a non-empty string");const y=a.findKey(s,u);(!y||s[y]===void 0||f===!0||f===void 0&&s[y]!==!1)&&(s[y||l]=le(c))}const i=(c,l)=>a.forEach(c,(f,u)=>o(f,u,l));if(a.isPlainObject(t)||t instanceof this.constructor)i(t,n);else if(a.isString(t)&&(t=t.trim())&&!On(t))i(mn(t),n);else if(a.isObject(t)&&a.isIterable(t)){let c={},l,f;for(const u of t){if(!a.isArray(u))throw TypeError("Object iterator must return a key-value pair");c[f=u[0]]=(l=c[f])?a.isArray(l)?[...l,u[1]]:[l,u[1]]:u[1]}i(c,n)}else t!=null&&o(n,t,r);return this}get(t,n){if(t=Y(t),t){const r=a.findKey(this,t);if(r){const s=this[r];if(!n)return s;if(n===!0)return gn(s);if(a.isFunction(n))return n.call(this,s,r);if(a.isRegExp(n))return n.exec(s);throw new TypeError("parser must be boolean|regexp|function")}}}has(t,n){if(t=Y(t),t){const r=a.findKey(this,t);return!!(r&&this[r]!==void 0&&(!n||ge(this,this[r],r,n)))}return!1}delete(t,n){const r=this;let s=!1;function o(i){if(i=Y(i),i){const c=a.findKey(r,i);c&&(!n||ge(r,r[c],c,n))&&(delete r[c],s=!0)}}return a.isArray(t)?t.forEach(o):o(t),s}clear(t){const n=Object.keys(this);let r=n.length,s=!1;for(;r--;){const o=n[r];(!t||ge(this,this[o],o,t,!0))&&(delete this[o],s=!0)}return s}normalize(t){const n=this,r={};return a.forEach(this,(s,o)=>{const i=a.findKey(r,o);if(i){n[i]=le(s),delete n[o];return}const c=t?Sn(o):String(o).trim();c!==o&&delete n[o],n[c]=le(s),r[c]=!0}),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const n=Object.create(null);return a.forEach(this,(r,s)=>{r!=null&&r!==!1&&(n[s]=t&&a.isArray(r)?r.join(", "):r)}),n}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([t,n])=>t+": "+n).join(` +`)}getSetCookie(){return this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(t){return t instanceof this?t:new this(t)}static concat(t,...n){const r=new this(t);return n.forEach(s=>r.set(s)),r}static accessor(t){const r=(this[$e]=this[$e]={accessors:{}}).accessors,s=this.prototype;function o(i){const c=Y(i);r[c]||(An(s,i),r[c]=!0)}return a.isArray(t)?t.forEach(o):o(t),this}};x.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);a.reduceDescriptors(x.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(r){this[n]=r}}});a.freezeMethods(x);const _n="[REDACTED ****]";function Tn(e){if(a.hasOwnProp(e,"toJSON"))return!0;let t=Object.getPrototypeOf(e);for(;t&&t!==Object.prototype;){if(a.hasOwnProp(t,"toJSON"))return!0;t=Object.getPrototypeOf(t)}return!1}function xn(e,t){const n=new Set(t.map(o=>String(o).toLowerCase())),r=[],s=o=>{if(o===null||typeof o!="object"||a.isBuffer(o))return o;if(r.indexOf(o)!==-1)return;o instanceof x&&(o=o.toJSON()),r.push(o);let i;if(a.isArray(o))i=[],o.forEach((c,l)=>{const f=s(c);a.isUndefined(f)||(i[l]=f)});else{if(!a.isPlainObject(o)&&Tn(o))return r.pop(),o;i=Object.create(null);for(const[c,l]of Object.entries(o)){const f=n.has(c.toLowerCase())?_n:s(l);a.isUndefined(f)||(i[c]=f)}}return r.pop(),i};return s(e)}let p=class ut extends Error{static from(t,n,r,s,o,i){const c=new ut(t.message,n||t.code,r,s,o);return c.cause=t,c.name=t.name,t.status!=null&&c.status==null&&(c.status=t.status),i&&Object.assign(c,i),c}constructor(t,n,r,s,o){super(t),Object.defineProperty(this,"message",{__proto__:null,value:t,enumerable:!0,writable:!0,configurable:!0}),this.name="AxiosError",this.isAxiosError=!0,n&&(this.code=n),r&&(this.config=r),s&&(this.request=s),o&&(this.response=o,this.status=o.status)}toJSON(){const t=this.config,n=t&&a.hasOwnProp(t,"redact")?t.redact:void 0,r=a.isArray(n)&&n.length>0?xn(t,n):a.toJSONObject(t);return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:r,code:this.code,status:this.status}}};p.ERR_BAD_OPTION_VALUE="ERR_BAD_OPTION_VALUE";p.ERR_BAD_OPTION="ERR_BAD_OPTION";p.ECONNABORTED="ECONNABORTED";p.ETIMEDOUT="ETIMEDOUT";p.ECONNREFUSED="ECONNREFUSED";p.ERR_NETWORK="ERR_NETWORK";p.ERR_FR_TOO_MANY_REDIRECTS="ERR_FR_TOO_MANY_REDIRECTS";p.ERR_DEPRECATED="ERR_DEPRECATED";p.ERR_BAD_RESPONSE="ERR_BAD_RESPONSE";p.ERR_BAD_REQUEST="ERR_BAD_REQUEST";p.ERR_CANCELED="ERR_CANCELED";p.ERR_NOT_SUPPORT="ERR_NOT_SUPPORT";p.ERR_INVALID_URL="ERR_INVALID_URL";p.ERR_FORM_DATA_DEPTH_EXCEEDED="ERR_FORM_DATA_DEPTH_EXCEEDED";const Cn=null;function xe(e){return a.isPlainObject(e)||a.isArray(e)}function ft(e){return a.endsWith(e,"[]")?e.slice(0,-2):e}function Oe(e,t,n){return e?e.concat(t).map(function(s,o){return s=ft(s),!n&&o?"["+s+"]":s}).join(n?".":""):t}function Nn(e){return a.isArray(e)&&!e.some(xe)}const Pn=a.toFlatObject(a,{},null,function(t){return/^is[A-Z]/.test(t)});function ye(e,t,n){if(!a.isObject(e))throw new TypeError("target must be an object");t=t||new FormData,n=a.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(d,m){return!a.isUndefined(m[d])});const r=n.metaTokens,s=n.visitor||y,o=n.dots,i=n.indexes,c=n.Blob||typeof Blob<"u"&&Blob,l=n.maxDepth===void 0?100:n.maxDepth,f=c&&a.isSpecCompliantForm(t);if(!a.isFunction(s))throw new TypeError("visitor must be a function");function u(h){if(h===null)return"";if(a.isDate(h))return h.toISOString();if(a.isBoolean(h))return h.toString();if(!f&&a.isBlob(h))throw new p("Blob is not supported. Use a Buffer instead.");return a.isArrayBuffer(h)||a.isTypedArray(h)?f&&typeof Blob=="function"?new Blob([h]):Buffer.from(h):h}function y(h,d,m){let O=h;if(a.isReactNative(t)&&a.isReactNativeBlob(h))return t.append(Oe(m,d,o),u(h)),!1;if(h&&!m&&typeof h=="object"){if(a.endsWith(d,"{}"))d=r?d:d.slice(0,-2),h=JSON.stringify(h);else if(a.isArray(h)&&Nn(h)||(a.isFileList(h)||a.endsWith(d,"[]"))&&(O=a.toArray(h)))return d=ft(d),O.forEach(function(R,N){!(a.isUndefined(R)||R===null)&&t.append(i===!0?Oe([d],N,o):i===null?d:d+"[]",u(R))}),!1}return xe(h)?!0:(t.append(Oe(m,d,o),u(h)),!1)}const E=[],b=Object.assign(Pn,{defaultVisitor:y,convertValue:u,isVisitable:xe});function w(h,d,m=0){if(!a.isUndefined(h)){if(m>l)throw new p("Object is too deeply nested ("+m+" levels). Max depth: "+l,p.ERR_FORM_DATA_DEPTH_EXCEEDED);if(E.indexOf(h)!==-1)throw Error("Circular reference detected in "+d.join("."));E.push(h),a.forEach(h,function(g,R){(!(a.isUndefined(g)||g===null)&&s.call(t,g,a.isString(R)?R.trim():R,d,b))===!0&&w(g,d?d.concat(R):[R],m+1)}),E.pop()}}if(!a.isObject(e))throw new TypeError("data must be an object");return w(e),t}function Ve(e){const t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+"};return encodeURIComponent(e).replace(/[!'()~]|%20/g,function(r){return t[r]})}function De(e,t){this._pairs=[],e&&ye(e,this,t)}const dt=De.prototype;dt.append=function(t,n){this._pairs.push([t,n])};dt.toString=function(t){const n=t?function(r){return t.call(this,r,Ve)}:Ve;return this._pairs.map(function(s){return n(s[0])+"="+n(s[1])},"").join("&")};function Dn(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+")}function pt(e,t,n){if(!t)return e;const r=n&&n.encode||Dn,s=a.isFunction(n)?{serialize:n}:n,o=s&&s.serialize;let i;if(o?i=o(t,s):i=a.isURLSearchParams(t)?t.toString():new De(t,s).toString(r),i){const c=e.indexOf("#");c!==-1&&(e=e.slice(0,c)),e+=(e.indexOf("?")===-1?"?":"&")+i}return e}class Je{constructor(){this.handlers=[]}use(t,n,r){return this.handlers.push({fulfilled:t,rejected:n,synchronous:r?r.synchronous:!1,runWhen:r?r.runWhen:null}),this.handlers.length-1}eject(t){this.handlers[t]&&(this.handlers[t]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(t){a.forEach(this.handlers,function(r){r!==null&&t(r)})}}const Le={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1,legacyInterceptorReqResOrdering:!0},Ln=typeof URLSearchParams<"u"?URLSearchParams:De,Fn=typeof FormData<"u"?FormData:null,Un=typeof Blob<"u"?Blob:null,Bn={isBrowser:!0,classes:{URLSearchParams:Ln,FormData:Fn,Blob:Un},protocols:["http","https","file","blob","url","data"]},Fe=typeof window<"u"&&typeof document<"u",Ce=typeof navigator=="object"&&navigator||void 0,kn=Fe&&(!Ce||["ReactNative","NativeScript","NS"].indexOf(Ce.product)<0),jn=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope&&typeof self.importScripts=="function",qn=Fe&&window.location.href||"http://localhost",In=Object.freeze(Object.defineProperty({__proto__:null,hasBrowserEnv:Fe,hasStandardBrowserEnv:kn,hasStandardBrowserWebWorkerEnv:jn,navigator:Ce,origin:qn},Symbol.toStringTag,{value:"Module"})),T={...In,...Bn};function Hn(e,t){return ye(e,new T.classes.URLSearchParams,{visitor:function(n,r,s,o){return T.isNode&&a.isBuffer(n)?(this.append(r,n.toString("base64")),!1):o.defaultVisitor.apply(this,arguments)},...t})}function Mn(e){return a.matchAll(/\w+|\[(\w*)]/g,e).map(t=>t[0]==="[]"?"":t[1]||t[0])}function zn(e){const t={},n=Object.keys(e);let r;const s=n.length;let o;for(r=0;r=n.length;return i=!i&&a.isArray(s)?s.length:i,l?(a.hasOwnProp(s,i)?s[i]=a.isArray(s[i])?s[i].concat(r):[s[i],r]:s[i]=r,!c):((!a.hasOwnProp(s,i)||!a.isObject(s[i]))&&(s[i]=[]),t(n,r,s[i],o)&&a.isArray(s[i])&&(s[i]=zn(s[i])),!c)}if(a.isFormData(e)&&a.isFunction(e.entries)){const n={};return a.forEachEntry(e,(r,s)=>{t(Mn(r),s,n,0)}),n}return null}const W=(e,t)=>e!=null&&a.hasOwnProp(e,t)?e[t]:void 0;function $n(e,t,n){if(a.isString(e))try{return(t||JSON.parse)(e),a.trim(e)}catch(r){if(r.name!=="SyntaxError")throw r}return(n||JSON.stringify)(e)}const ne={transitional:Le,adapter:["xhr","http","fetch"],transformRequest:[function(t,n){const r=n.getContentType()||"",s=r.indexOf("application/json")>-1,o=a.isObject(t);if(o&&a.isHTMLForm(t)&&(t=new FormData(t)),a.isFormData(t))return s?JSON.stringify(ht(t)):t;if(a.isArrayBuffer(t)||a.isBuffer(t)||a.isStream(t)||a.isFile(t)||a.isBlob(t)||a.isReadableStream(t))return t;if(a.isArrayBufferView(t))return t.buffer;if(a.isURLSearchParams(t))return n.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),t.toString();let c;if(o){const l=W(this,"formSerializer");if(r.indexOf("application/x-www-form-urlencoded")>-1)return Hn(t,l).toString();if((c=a.isFileList(t))||r.indexOf("multipart/form-data")>-1){const f=W(this,"env"),u=f&&f.FormData;return ye(c?{"files[]":t}:t,u&&new u,l)}}return o||s?(n.setContentType("application/json",!1),$n(t)):t}],transformResponse:[function(t){const n=W(this,"transitional")||ne.transitional,r=n&&n.forcedJSONParsing,s=W(this,"responseType"),o=s==="json";if(a.isResponse(t)||a.isReadableStream(t))return t;if(t&&a.isString(t)&&(r&&!s||o)){const c=!(n&&n.silentJSONParsing)&&o;try{return JSON.parse(t,W(this,"parseReviver"))}catch(l){if(c)throw l.name==="SyntaxError"?p.from(l,p.ERR_BAD_RESPONSE,this,null,W(this,"response")):l}}return t}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:T.classes.FormData,Blob:T.classes.Blob},validateStatus:function(t){return t>=200&&t<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};a.forEach(["delete","get","head","post","put","patch","query"],e=>{ne.headers[e]={}});function Se(e,t){const n=this||ne,r=t||n,s=x.from(r.headers);let o=r.data;return a.forEach(e,function(c){o=c.call(n,o,s.normalize(),t?t.status:void 0)}),s.normalize(),o}function mt(e){return!!(e&&e.__CANCEL__)}let re=class extends p{constructor(t,n,r){super(t??"canceled",p.ERR_CANCELED,n,r),this.name="CanceledError",this.__CANCEL__=!0}};function yt(e,t,n){const r=n.config.validateStatus;!n.status||!r||r(n.status)?e(n):t(new p("Request failed with status code "+n.status,n.status>=400&&n.status<500?p.ERR_BAD_REQUEST:p.ERR_BAD_RESPONSE,n.config,n.request,n))}function Vn(e){const t=/^([-+\w]{1,25}):(?:\/\/)?/.exec(e);return t&&t[1]||""}function Jn(e,t){e=e||10;const n=new Array(e),r=new Array(e);let s=0,o=0,i;return t=t!==void 0?t:1e3,function(l){const f=Date.now(),u=r[o];i||(i=f),n[s]=l,r[s]=f;let y=o,E=0;for(;y!==s;)E+=n[y++],y=y%e;if(s=(s+1)%e,s===o&&(o=(o+1)%e),f-i{n=u,s=null,o&&(clearTimeout(o),o=null),e(...f)};return[(...f)=>{const u=Date.now(),y=u-n;y>=r?i(f,u):(s=f,o||(o=setTimeout(()=>{o=null,i(s)},r-y)))},()=>s&&i(s)]}const fe=(e,t,n=3)=>{let r=0;const s=Jn(50,250);return Wn(o=>{if(!o||typeof o.loaded!="number")return;const i=o.loaded,c=o.lengthComputable?o.total:void 0,l=c!=null?Math.min(i,c):i,f=Math.max(0,l-r),u=s(f);r=Math.max(r,l);const y={loaded:l,total:c,progress:c?l/c:void 0,bytes:f,rate:u||void 0,estimated:u&&c?(c-l)/u:void 0,event:o,lengthComputable:c!=null,[t?"download":"upload"]:!0};e(y)},n)},We=(e,t)=>{const n=e!=null;return[r=>t[0]({lengthComputable:n,total:e,loaded:r}),t[1]]},Ke=e=>(...t)=>a.asap(()=>e(...t)),Kn=T.hasStandardBrowserEnv?((e,t)=>n=>(n=new URL(n,T.origin),e.protocol===n.protocol&&e.host===n.host&&(t||e.port===n.port)))(new URL(T.origin),T.navigator&&/(msie|trident)/i.test(T.navigator.userAgent)):()=>!0,vn=T.hasStandardBrowserEnv?{write(e,t,n,r,s,o,i){if(typeof document>"u")return;const c=[`${e}=${encodeURIComponent(t)}`];a.isNumber(n)&&c.push(`expires=${new Date(n).toUTCString()}`),a.isString(r)&&c.push(`path=${r}`),a.isString(s)&&c.push(`domain=${s}`),o===!0&&c.push("secure"),a.isString(i)&&c.push(`SameSite=${i}`),document.cookie=c.join("; ")},read(e){if(typeof document>"u")return null;const t=document.cookie.split(";");for(let n=0;ne instanceof x?{...e}:e;function V(e,t){t=t||{};const n=Object.create(null);Object.defineProperty(n,"hasOwnProperty",{__proto__:null,value:Object.prototype.hasOwnProperty,enumerable:!1,writable:!0,configurable:!0});function r(f,u,y,E){return a.isPlainObject(f)&&a.isPlainObject(u)?a.merge.call({caseless:E},f,u):a.isPlainObject(u)?a.merge({},u):a.isArray(u)?u.slice():u}function s(f,u,y,E){if(a.isUndefined(u)){if(!a.isUndefined(f))return r(void 0,f,y,E)}else return r(f,u,y,E)}function o(f,u){if(!a.isUndefined(u))return r(void 0,u)}function i(f,u){if(a.isUndefined(u)){if(!a.isUndefined(f))return r(void 0,f)}else return r(void 0,u)}function c(f,u,y){if(a.hasOwnProp(t,y))return r(f,u);if(a.hasOwnProp(e,y))return r(void 0,f)}const l={url:o,method:o,data:o,baseURL:i,transformRequest:i,transformResponse:i,paramsSerializer:i,timeout:i,timeoutMessage:i,withCredentials:i,withXSRFToken:i,adapter:i,responseType:i,xsrfCookieName:i,xsrfHeaderName:i,onUploadProgress:i,onDownloadProgress:i,decompress:i,maxContentLength:i,maxBodyLength:i,beforeRedirect:i,transport:i,httpAgent:i,httpsAgent:i,cancelToken:i,socketPath:i,allowedSocketPaths:i,responseEncoding:i,validateStatus:c,headers:(f,u,y)=>s(ve(f),ve(u),y,!0)};return a.forEach(Object.keys({...e,...t}),function(u){if(u==="__proto__"||u==="constructor"||u==="prototype")return;const y=a.hasOwnProp(l,u)?l[u]:s,E=a.hasOwnProp(e,u)?e[u]:void 0,b=a.hasOwnProp(t,u)?t[u]:void 0,w=y(E,b,u);a.isUndefined(w)&&y!==c||(n[u]=w)}),n}const Qn=["content-type","content-length"];function Yn(e,t,n){if(n!=="content-only"){e.set(t);return}Object.entries(t).forEach(([r,s])=>{Qn.includes(r.toLowerCase())&&e.set(r,s)})}const Zn=e=>encodeURIComponent(e).replace(/%([0-9A-F]{2})/gi,(t,n)=>String.fromCharCode(parseInt(n,16))),wt=e=>{const t=V({},e),n=E=>a.hasOwnProp(t,E)?t[E]:void 0,r=n("data");let s=n("withXSRFToken");const o=n("xsrfHeaderName"),i=n("xsrfCookieName");let c=n("headers");const l=n("auth"),f=n("baseURL"),u=n("allowAbsoluteUrls"),y=n("url");if(t.headers=c=x.from(c),t.url=pt(bt(f,y,u),e.params,e.paramsSerializer),l&&c.set("Authorization","Basic "+btoa((l.username||"")+":"+(l.password?Zn(l.password):""))),a.isFormData(r)&&(T.hasStandardBrowserEnv||T.hasStandardBrowserWebWorkerEnv?c.setContentType(void 0):a.isFunction(r.getHeaders)&&Yn(c,r.getHeaders(),n("formDataHeaderPolicy"))),T.hasStandardBrowserEnv&&(a.isFunction(s)&&(s=s(t)),s===!0||s==null&&Kn(t.url))){const b=o&&i&&vn.read(i);b&&c.set(o,b)}return t},er=typeof XMLHttpRequest<"u",tr=er&&function(e){return new Promise(function(n,r){const s=wt(e);let o=s.data;const i=x.from(s.headers).normalize();let{responseType:c,onUploadProgress:l,onDownloadProgress:f}=s,u,y,E,b,w;function h(){b&&b(),w&&w(),s.cancelToken&&s.cancelToken.unsubscribe(u),s.signal&&s.signal.removeEventListener("abort",u)}let d=new XMLHttpRequest;d.open(s.method.toUpperCase(),s.url,!0),d.timeout=s.timeout;function m(){if(!d)return;const g=x.from("getAllResponseHeaders"in d&&d.getAllResponseHeaders()),N={data:!c||c==="text"||c==="json"?d.responseText:d.response,status:d.status,statusText:d.statusText,headers:g,config:e,request:d};yt(function(X){n(X),h()},function(X){r(X),h()},N),d=null}"onloadend"in d?d.onloadend=m:d.onreadystatechange=function(){!d||d.readyState!==4||d.status===0&&!(d.responseURL&&d.responseURL.startsWith("file:"))||setTimeout(m)},d.onabort=function(){d&&(r(new p("Request aborted",p.ECONNABORTED,e,d)),h(),d=null)},d.onerror=function(R){const N=R&&R.message?R.message:"Network Error",I=new p(N,p.ERR_NETWORK,e,d);I.event=R||null,r(I),h(),d=null},d.ontimeout=function(){let R=s.timeout?"timeout of "+s.timeout+"ms exceeded":"timeout exceeded";const N=s.transitional||Le;s.timeoutErrorMessage&&(R=s.timeoutErrorMessage),r(new p(R,N.clarifyTimeoutError?p.ETIMEDOUT:p.ECONNABORTED,e,d)),h(),d=null},o===void 0&&i.setContentType(null),"setRequestHeader"in d&&a.forEach(lt(i),function(R,N){d.setRequestHeader(N,R)}),a.isUndefined(s.withCredentials)||(d.withCredentials=!!s.withCredentials),c&&c!=="json"&&(d.responseType=s.responseType),f&&([E,w]=fe(f,!0),d.addEventListener("progress",E)),l&&d.upload&&([y,b]=fe(l),d.upload.addEventListener("progress",y),d.upload.addEventListener("loadend",b)),(s.cancelToken||s.signal)&&(u=g=>{d&&(r(!g||g.type?new re(null,e,d):g),d.abort(),h(),d=null)},s.cancelToken&&s.cancelToken.subscribe(u),s.signal&&(s.signal.aborted?u():s.signal.addEventListener("abort",u)));const O=Vn(s.url);if(O&&!T.protocols.includes(O)){r(new p("Unsupported protocol "+O+":",p.ERR_BAD_REQUEST,e));return}d.send(o||null)})},nr=(e,t)=>{if(e=e?e.filter(Boolean):[],!t&&!e.length)return;const n=new AbortController;let r=!1;const s=function(l){if(!r){r=!0,i();const f=l instanceof Error?l:this.reason;n.abort(f instanceof p?f:new re(f instanceof Error?f.message:f))}};let o=t&&setTimeout(()=>{o=null,s(new p(`timeout of ${t}ms exceeded`,p.ETIMEDOUT))},t);const i=()=>{e&&(o&&clearTimeout(o),o=null,e.forEach(l=>{l.unsubscribe?l.unsubscribe(s):l.removeEventListener("abort",s)}),e=null)};e.forEach(l=>l.addEventListener("abort",s));const{signal:c}=n;return c.unsubscribe=()=>a.asap(i),c},rr=function*(e,t){let n=e.byteLength;if(n{const s=sr(e,t);let o=0,i,c=l=>{i||(i=!0,r&&r(l))};return new ReadableStream({async pull(l){try{const{done:f,value:u}=await s.next();if(f){c(),l.close();return}let y=u.byteLength;if(n){let E=o+=y;n(E)}l.enqueue(new Uint8Array(u))}catch(f){throw c(f),f}},cancel(l){return c(l),s.return()}},{highWaterMark:2})};function ir(e){if(!e||typeof e!="string"||!e.startsWith("data:"))return 0;const t=e.indexOf(",");if(t<0)return 0;const n=e.slice(5,t),r=e.slice(t+1);if(/;base64/i.test(n)){let i=r.length;const c=r.length;for(let b=0;b=48&&w<=57||w>=65&&w<=70||w>=97&&w<=102)&&(h>=48&&h<=57||h>=65&&h<=70||h>=97&&h<=102)&&(i-=2,b+=2)}let l=0,f=c-1;const u=b=>b>=2&&r.charCodeAt(b-2)===37&&r.charCodeAt(b-1)===51&&(r.charCodeAt(b)===68||r.charCodeAt(b)===100);f>=0&&(r.charCodeAt(f)===61?(l++,f--):u(f)&&(l++,f-=3)),l===1&&f>=0&&(r.charCodeAt(f)===61||u(f))&&l++;const E=Math.floor(i/4)*3-(l||0);return E>0?E:0}if(typeof Buffer<"u"&&typeof Buffer.byteLength=="function")return Buffer.byteLength(r,"utf8");let o=0;for(let i=0,c=r.length;i=55296&&l<=56319&&i+1=56320&&f<=57343?(o+=4,i++):o+=3}else o+=3}return o}const Ue="1.16.1",Ge=64*1024,{isFunction:ae}=a,Qe=(e,...t)=>{try{return!!e(...t)}catch{return!1}},ar=e=>{const t=a.global!==void 0&&a.global!==null?a.global:globalThis,{ReadableStream:n,TextEncoder:r}=t;e=a.merge.call({skipUndefined:!0},{Request:t.Request,Response:t.Response},e);const{fetch:s,Request:o,Response:i}=e,c=s?ae(s):typeof fetch=="function",l=ae(o),f=ae(i);if(!c)return!1;const u=c&&ae(n),y=c&&(typeof r=="function"?(m=>O=>m.encode(O))(new r):async m=>new Uint8Array(await new o(m).arrayBuffer())),E=l&&u&&Qe(()=>{let m=!1;const O=new o(T.origin,{body:new n,method:"POST",get duplex(){return m=!0,"half"}}),g=O.headers.has("Content-Type");return O.body!=null&&O.body.cancel(),m&&!g}),b=f&&u&&Qe(()=>a.isReadableStream(new i("").body)),w={stream:b&&(m=>m.body)};c&&["text","arrayBuffer","blob","formData","stream"].forEach(m=>{!w[m]&&(w[m]=(O,g)=>{let R=O&&O[m];if(R)return R.call(O);throw new p(`Response type '${m}' is not supported`,p.ERR_NOT_SUPPORT,g)})});const h=async m=>{if(m==null)return 0;if(a.isBlob(m))return m.size;if(a.isSpecCompliantForm(m))return(await new o(T.origin,{method:"POST",body:m}).arrayBuffer()).byteLength;if(a.isArrayBufferView(m)||a.isArrayBuffer(m))return m.byteLength;if(a.isURLSearchParams(m)&&(m=m+""),a.isString(m))return(await y(m)).byteLength},d=async(m,O)=>{const g=a.toFiniteNumber(m.getContentLength());return g??h(O)};return async m=>{let{url:O,method:g,data:R,signal:N,cancelToken:I,timeout:X,onDownloadProgress:we,onUploadProgress:ke,responseType:k,headers:H,withCredentials:se="same-origin",fetchOptions:je,maxContentLength:F,maxBodyLength:Ee}=wt(m);const G=a.isNumber(F)&&F>-1,St=a.isNumber(Ee)&&Ee>-1;let qe=s||fetch;k=k?(k+"").toLowerCase():"text";let j=nr([N,I&&I.toAbortSignal()],X),P=null;const M=j&&j.unsubscribe&&(()=>{j.unsubscribe()});let Ie;try{if(G&&typeof O=="string"&&O.startsWith("data:")&&ir(O)>F)throw new p("maxContentLength size of "+F+" exceeded",p.ERR_BAD_RESPONSE,m,P);if(St&&g!=="get"&&g!=="head"){const S=await d(H,R);if(typeof S=="number"&&isFinite(S)&&S>Ee)throw new p("Request body larger than maxBodyLength limit",p.ERR_BAD_REQUEST,m,P)}if(ke&&E&&g!=="get"&&g!=="head"&&(Ie=await d(H,R))!==0){let S=new o(O,{method:"POST",body:R,duplex:"half"}),J;if(a.isFormData(R)&&(J=S.headers.get("content-type"))&&H.setContentType(J),S.body){const[oe,ie]=We(Ie,fe(Ke(ke)));R=Xe(S.body,Ge,oe,ie)}}a.isString(se)||(se=se?"include":"omit");const _=l&&"credentials"in o.prototype;if(a.isFormData(R)){const S=H.getContentType();S&&/^multipart\/form-data/i.test(S)&&!/boundary=/i.test(S)&&H.delete("content-type")}H.set("User-Agent","axios/"+Ue,!1);const q={...je,signal:j,method:g.toUpperCase(),headers:lt(H.normalize()),body:R,duplex:"half",credentials:_?se:void 0};P=l&&new o(O,q);let U=await(l?qe(P,je):qe(O,q));if(G){const S=a.toFiniteNumber(U.headers.get("content-length"));if(S!=null&&S>F)throw new p("maxContentLength size of "+F+" exceeded",p.ERR_BAD_RESPONSE,m,P)}const Re=b&&(k==="stream"||k==="response");if(b&&U.body&&(we||G||Re&&M)){const S={};["status","statusText","headers"].forEach(Q=>{S[Q]=U[Q]});const J=a.toFiniteNumber(U.headers.get("content-length")),[oe,ie]=we&&We(J,fe(Ke(we),!0))||[];let He=0;const At=Q=>{if(G&&(He=Q,He>F))throw new p("maxContentLength size of "+F+" exceeded",p.ERR_BAD_RESPONSE,m,P);oe&&oe(Q)};U=new i(Xe(U.body,Ge,At,()=>{ie&&ie(),M&&M()}),S)}k=k||"text";let B=await w[a.findKey(w,k)||"text"](U,m);if(G&&!b&&!Re){let S;if(B!=null&&(typeof B.byteLength=="number"?S=B.byteLength:typeof B.size=="number"?S=B.size:typeof B=="string"&&(S=typeof r=="function"?new r().encode(B).byteLength:B.length)),typeof S=="number"&&S>F)throw new p("maxContentLength size of "+F+" exceeded",p.ERR_BAD_RESPONSE,m,P)}return!Re&&M&&M(),await new Promise((S,J)=>{yt(S,J,{data:B,headers:x.from(U.headers),status:U.status,statusText:U.statusText,config:m,request:P})})}catch(_){if(M&&M(),j&&j.aborted&&j.reason instanceof p){const q=j.reason;throw q.config=m,P&&(q.request=P),_!==q&&(q.cause=_),q}throw _&&_.name==="TypeError"&&/Load failed|fetch/i.test(_.message)?Object.assign(new p("Network Error",p.ERR_NETWORK,m,P,_&&_.response),{cause:_.cause||_}):p.from(_,_&&_.code,m,P,_&&_.response)}}},cr=new Map,Et=e=>{let t=e&&e.env||{};const{fetch:n,Request:r,Response:s}=t,o=[r,s,n];let i=o.length,c=i,l,f,u=cr;for(;c--;)l=o[c],f=u.get(l),f===void 0&&u.set(l,f=c?new Map:ar(t)),u=f;return f};Et();const Be={http:Cn,xhr:tr,fetch:{get:Et}};a.forEach(Be,(e,t)=>{if(e){try{Object.defineProperty(e,"name",{__proto__:null,value:t})}catch{}Object.defineProperty(e,"adapterName",{__proto__:null,value:t})}});const Ye=e=>`- ${e}`,lr=e=>a.isFunction(e)||e===null||e===!1;function ur(e,t){e=a.isArray(e)?e:[e];const{length:n}=e;let r,s;const o={};for(let i=0;i`adapter ${l} `+(f===!1?"is not supported by the environment":"is not available in the build"));let c=n?i.length>1?`since : +`+i.map(Ye).join(` +`):" "+Ye(i[0]):"as no adapter specified";throw new p("There is no suitable adapter to dispatch the request "+c,"ERR_NOT_SUPPORT")}return s}const Rt={getAdapter:ur,adapters:Be};function Ae(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new re(null,e)}function Ze(e){return Ae(e),e.headers=x.from(e.headers),e.data=Se.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),Rt.getAdapter(e.adapter||ne.adapter,e)(e).then(function(r){Ae(e),e.response=r;try{r.data=Se.call(e,e.transformResponse,r)}finally{delete e.response}return r.headers=x.from(r.headers),r},function(r){if(!mt(r)&&(Ae(e),r&&r.response)){e.response=r.response;try{r.response.data=Se.call(e,e.transformResponse,r.response)}finally{delete e.response}r.response.headers=x.from(r.response.headers)}return Promise.reject(r)})}const be={};["object","boolean","number","function","string","symbol"].forEach((e,t)=>{be[e]=function(r){return typeof r===e||"a"+(t<1?"n ":" ")+e}});const et={};be.transitional=function(t,n,r){function s(o,i){return"[Axios v"+Ue+"] Transitional option '"+o+"'"+i+(r?". "+r:"")}return(o,i,c)=>{if(t===!1)throw new p(s(i," has been removed"+(n?" in "+n:"")),p.ERR_DEPRECATED);return n&&!et[i]&&(et[i]=!0,console.warn(s(i," has been deprecated since v"+n+" and will be removed in the near future"))),t?t(o,i,c):!0}};be.spelling=function(t){return(n,r)=>(console.warn(`${r} is likely a misspelling of ${t}`),!0)};function fr(e,t,n){if(typeof e!="object")throw new p("options must be an object",p.ERR_BAD_OPTION_VALUE);const r=Object.keys(e);let s=r.length;for(;s-- >0;){const o=r[s],i=Object.prototype.hasOwnProperty.call(t,o)?t[o]:void 0;if(i){const c=e[o],l=c===void 0||i(c,o,e);if(l!==!0)throw new p("option "+o+" must be "+l,p.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new p("Unknown option "+o,p.ERR_BAD_OPTION)}}const ue={assertOptions:fr,validators:be},D=ue.validators;let $=class{constructor(t){this.defaults=t||{},this.interceptors={request:new Je,response:new Je}}async request(t,n){try{return await this._request(t,n)}catch(r){if(r instanceof Error){let s={};Error.captureStackTrace?Error.captureStackTrace(s):s=new Error;const o=(()=>{if(!s.stack)return"";const i=s.stack.indexOf(` +`);return i===-1?"":s.stack.slice(i+1)})();try{if(!r.stack)r.stack=o;else if(o){const i=o.indexOf(` +`),c=i===-1?-1:o.indexOf(` +`,i+1),l=c===-1?"":o.slice(c+1);String(r.stack).endsWith(l)||(r.stack+=` +`+o)}}catch{}}throw r}}_request(t,n){typeof t=="string"?(n=n||{},n.url=t):n=t||{},n=V(this.defaults,n);const{transitional:r,paramsSerializer:s,headers:o}=n;r!==void 0&&ue.assertOptions(r,{silentJSONParsing:D.transitional(D.boolean),forcedJSONParsing:D.transitional(D.boolean),clarifyTimeoutError:D.transitional(D.boolean),legacyInterceptorReqResOrdering:D.transitional(D.boolean)},!1),s!=null&&(a.isFunction(s)?n.paramsSerializer={serialize:s}:ue.assertOptions(s,{encode:D.function,serialize:D.function},!0)),n.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls!==void 0?n.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:n.allowAbsoluteUrls=!0),ue.assertOptions(n,{baseUrl:D.spelling("baseURL"),withXsrfToken:D.spelling("withXSRFToken")},!0),n.method=(n.method||this.defaults.method||"get").toLowerCase();let i=o&&a.merge(o.common,o[n.method]);o&&a.forEach(["delete","get","head","post","put","patch","query","common"],w=>{delete o[w]}),n.headers=x.concat(i,o);const c=[];let l=!0;this.interceptors.request.forEach(function(h){if(typeof h.runWhen=="function"&&h.runWhen(n)===!1)return;l=l&&h.synchronous;const d=n.transitional||Le;d&&d.legacyInterceptorReqResOrdering?c.unshift(h.fulfilled,h.rejected):c.push(h.fulfilled,h.rejected)});const f=[];this.interceptors.response.forEach(function(h){f.push(h.fulfilled,h.rejected)});let u,y=0,E;if(!l){const w=[Ze.bind(this),void 0];for(w.unshift(...c),w.push(...f),E=w.length,u=Promise.resolve(n);y{if(!r._listeners)return;let o=r._listeners.length;for(;o-- >0;)r._listeners[o](s);r._listeners=null}),this.promise.then=s=>{let o;const i=new Promise(c=>{r.subscribe(c),o=c}).then(s);return i.cancel=function(){r.unsubscribe(o)},i},t(function(o,i,c){r.reason||(r.reason=new re(o,i,c),n(r.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const n=this._listeners.indexOf(t);n!==-1&&this._listeners.splice(n,1)}toAbortSignal(){const t=new AbortController,n=r=>{t.abort(r)};return this.subscribe(n),t.signal.unsubscribe=()=>this.unsubscribe(n),t.signal}static source(){let t;return{token:new gt(function(s){t=s}),cancel:t}}};function pr(e){return function(n){return e.apply(null,n)}}function hr(e){return a.isObject(e)&&e.isAxiosError===!0}const Ne={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(Ne).forEach(([e,t])=>{Ne[t]=e});function Ot(e){const t=new $(e),n=tt($.prototype.request,t);return a.extend(n,$.prototype,t,{allOwnKeys:!0}),a.extend(n,t,null,{allOwnKeys:!0}),n.create=function(s){return Ot(V(e,s))},n}const A=Ot(ne);A.Axios=$;A.CanceledError=re;A.CancelToken=dr;A.isCancel=mt;A.VERSION=Ue;A.toFormData=ye;A.AxiosError=p;A.Cancel=A.CanceledError;A.all=function(t){return Promise.all(t)};A.spread=pr;A.isAxiosError=hr;A.mergeConfig=V;A.AxiosHeaders=x;A.formToJSON=e=>ht(a.isHTMLForm(e)?new FormData(e):e);A.getAdapter=Rt.getAdapter;A.HttpStatusCode=Ne;A.default=A;const{Axios:wr,AxiosError:Er,CanceledError:Rr,isCancel:gr,CancelToken:Or,VERSION:Sr,all:Ar,Cancel:_r,isAxiosError:Tr,spread:xr,toFormData:Cr,AxiosHeaders:Nr,HttpStatusCode:Pr,formToJSON:Dr,getAdapter:Lr,mergeConfig:Fr,create:Ur}=A;export{A as a}; diff --git a/backend/src/main/resources/static/assets/index-Dk81znn6.css b/backend/src/main/resources/static/assets/index-Dk81znn6.css new file mode 100644 index 0000000..698c3d5 --- /dev/null +++ b/backend/src/main/resources/static/assets/index-Dk81znn6.css @@ -0,0 +1 @@ +.skip-link{position:absolute;top:-60px;left:0;z-index:9999;background:var(--primary);color:#fff;padding:10px 20px;border-radius:0 0 8px;transition:top .2s}.skip-link:focus{top:0}.header{position:fixed;top:0;left:0;right:0;z-index:1000;height:var(--header-h);background:#1a1a2ef5;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid rgba(255,255,255,.08);transition:all var(--mid) var(--ease)}.header.scrolled{background:#1a1a2efc;box-shadow:0 4px 24px #0000004d}.header-inner{display:flex;align-items:center;gap:32px;height:100%}.logo{display:flex;align-items:center;gap:10px;flex-shrink:0}.logo img{height:40px;width:auto;filter:brightness(0) invert(1)}.logo-text{color:#fff;font-size:20px;font-weight:700}.logo-text strong{color:var(--accent)}.nav-desktop{display:flex;align-items:center;gap:4px;margin-left:24px;flex:1}.nav-item{position:relative}.nav-trigger{height:var(--header-h);padding:0 16px;color:#ffffffd9;font-size:15px;font-weight:500;transition:color var(--fast);display:flex;align-items:center}.nav-trigger:hover,.nav-item.active .nav-trigger{color:#fff}.nav-item.active .nav-trigger{border-bottom:2px solid var(--accent)}.dropdown{position:absolute;top:calc(var(--header-h) - 2px);left:0;min-width:180px;background:#fff;border-radius:0 0 var(--radius) var(--radius);box-shadow:var(--shadow-lg);border-top:3px solid var(--primary);padding:8px 0;animation:fadeDown .18s ease}@keyframes fadeDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.dropdown-item{display:flex;align-items:center;gap:8px;padding:10px 20px;font-size:14px;color:var(--gray-700);transition:all var(--fast)}.dropdown-item:hover,.dropdown-item.current{background:var(--primary-light);color:var(--primary)}.header-cta{margin-left:auto;flex-shrink:0}.hamburger{display:none;flex-direction:column;gap:5px;padding:8px;margin-left:auto}.hamburger span{display:block;width:24px;height:2px;background:#fff;border-radius:2px;transition:all var(--mid)}.nav-mobile{display:none;flex-direction:column;background:var(--secondary);border-top:1px solid rgba(255,255,255,.1);max-height:calc(100vh - var(--header-h));overflow-y:auto}.mobile-group{border-bottom:1px solid rgba(255,255,255,.08)}.mobile-group-header{display:flex;align-items:center;padding:14px 24px;color:#ffffffd9;font-size:15px;font-weight:500;cursor:pointer}.mobile-children{background:#0003}.mobile-child{display:flex;align-items:center;gap:8px;padding:10px 36px;font-size:14px;color:#ffffffb3}.mobile-child:hover{color:#fff}@media (max-width: 1024px){.nav-desktop,.header-cta{display:none}.hamburger,.header.mobile-open .nav-mobile{display:flex}.header.mobile-open{height:auto}}.footer{background:var(--secondary);color:#fffc}.footer-top{padding:60px 0}.footer-top-inner{display:grid;grid-template-columns:280px repeat(4,1fr);gap:40px}.footer-logo{display:flex;align-items:center;gap:10px;margin-bottom:16px}.footer-logo img{height:36px}.footer-logo-text{font-size:20px;font-weight:700;color:#fff}.footer-logo-text strong{color:var(--accent)}.footer-tagline{font-size:13px;line-height:1.8;color:#fff9;margin-bottom:20px}.footer-contact-list{display:flex;flex-direction:column;gap:8px}.footer-contact-item{display:flex;gap:10px;font-size:13px}.contact-label{color:#fff6;min-width:60px}.footer-contact-item a{color:var(--accent)}.footer-contact-item a:hover{text-decoration:underline}.footer-menu-title{font-size:13px;font-weight:700;color:#fff;letter-spacing:.5px;text-transform:uppercase;margin-bottom:16px;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,.1)}.footer-menu-list{display:flex;flex-direction:column;gap:10px}.footer-menu-list a{font-size:13px;color:#fff9;transition:color var(--fast)}.footer-menu-list a:hover{color:var(--accent)}.footer-bottom{border-top:1px solid rgba(255,255,255,.08);padding:18px 0}.footer-bottom-inner{display:flex;align-items:center;gap:24px;font-size:12px;color:#fff6}.footer-legal{display:flex;gap:16px}.footer-legal a{color:#fff6}.footer-legal a:hover{color:#fffc}.footer-copyright{flex:1;text-align:center}.footer-powered{color:#ffffff4d}.footer-powered strong{color:var(--accent)}@media (max-width: 1024px){.footer-top-inner{grid-template-columns:1fr 1fr}.footer-brand{grid-column:1 / -1}}@media (max-width: 768px){.footer-top-inner{grid-template-columns:1fr 1fr}.footer-bottom-inner{flex-direction:column;text-align:center;gap:12px}.footer-copyright{order:-1}}:root{--primary: #0051A2;--primary-dark: #003A7A;--primary-light: #E8F0FA;--accent: #00A3E0;--accent-dark: #0080B0;--secondary: #1A1A2E;--gray-900: #111827;--gray-800: #1F2937;--gray-700: #374151;--gray-600: #4B5563;--gray-400: #9CA3AF;--gray-200: #E5E7EB;--gray-100: #F3F4F6;--gray-50: #F9FAFB;--white: #FFFFFF;--success: #10B981;--warning: #F59E0B;--danger: #EF4444;--font-sans: "Noto Sans KR", "Inter", -apple-system, sans-serif;--font-en: "Inter", sans-serif;--container: 1280px;--header-h: 72px;--radius-sm: 6px;--radius: 12px;--radius-lg: 20px;--ease: cubic-bezier(.4,0,.2,1);--fast: .15s;--mid: .3s;--slow: .5s;--shadow-sm: 0 1px 3px rgba(0,0,0,.1);--shadow: 0 4px 16px rgba(0,0,0,.12);--shadow-lg: 0 12px 40px rgba(0,0,0,.16)}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html{scroll-behavior:smooth;font-size:16px}body{font-family:var(--font-sans);color:var(--gray-800);background:var(--white);line-height:1.6;-webkit-font-smoothing:antialiased}img{max-width:100%;height:auto;display:block}a{color:inherit;text-decoration:none}ul,ol{list-style:none}button{cursor:pointer;border:none;background:none;font-family:inherit}.container{max-width:var(--container);margin:0 auto;padding:0 24px}.section{padding:80px 0}.section-sm{padding:48px 0}.section-lg{padding:120px 0}.section-header{text-align:center;margin-bottom:56px}.section-label{display:inline-block;font-size:13px;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--accent);margin-bottom:12px}.section-title{font-size:clamp(28px,4vw,44px);font-weight:900;color:var(--gray-900);line-height:1.2}.section-title em{color:var(--primary);font-style:normal}.section-desc{margin-top:16px;font-size:17px;color:var(--gray-600);max-width:600px;margin-left:auto;margin-right:auto}.btn{display:inline-flex;align-items:center;gap:8px;padding:12px 28px;border-radius:var(--radius);font-size:15px;font-weight:600;transition:all var(--mid) var(--ease);line-height:1}.btn-primary{background:var(--primary);color:var(--white)}.btn-primary:hover{background:var(--primary-dark);transform:translateY(-2px);box-shadow:0 8px 24px #0051a24d}.btn-outline{border:2px solid var(--primary);color:var(--primary)}.btn-outline:hover{background:var(--primary);color:var(--white)}.btn-white{background:var(--white);color:var(--primary);font-weight:700}.btn-white:hover{background:var(--gray-100);transform:translateY(-2px)}.btn-lg{padding:16px 36px;font-size:16px}.btn-sm{padding:8px 20px;font-size:13px}.card{background:var(--white);border-radius:var(--radius);box-shadow:var(--shadow-sm);border:1px solid var(--gray-200);transition:all var(--mid) var(--ease);overflow:hidden}.card:hover{box-shadow:var(--shadow-lg);transform:translateY(-4px);border-color:var(--primary-light)}.grid-2{display:grid;grid-template-columns:repeat(2,1fr);gap:24px}.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}.grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:24px}.badge{display:inline-block;padding:3px 10px;border-radius:20px;font-size:12px;font-weight:600}.badge-primary{background:var(--primary-light);color:var(--primary)}.badge-accent{background:#00a3e01f;color:var(--accent-dark)}.badge-new{background:var(--danger);color:var(--white)}.divider{width:48px;height:4px;background:var(--accent);border-radius:2px;margin:16px auto 0}.divider-left{margin-left:0}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:var(--gray-100)}::-webkit-scrollbar-thumb{background:var(--gray-400);border-radius:3px}@keyframes fadeUp{0%{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}.fade-up{animation:fadeUp var(--slow) var(--ease) both}@media (max-width: 1024px){.grid-4{grid-template-columns:repeat(2,1fr)}}@media (max-width: 768px){.section{padding:60px 0}.grid-2,.grid-3,.grid-4{grid-template-columns:1fr}.container{padding:0 16px}} diff --git a/backend/src/main/resources/static/favicon.ico b/backend/src/main/resources/static/favicon.ico new file mode 100644 index 0000000..3252b9f Binary files /dev/null and b/backend/src/main/resources/static/favicon.ico differ diff --git a/backend/src/main/resources/static/index.html b/backend/src/main/resources/static/index.html new file mode 100644 index 0000000..7416112 --- /dev/null +++ b/backend/src/main/resources/static/index.html @@ -0,0 +1,22 @@ + + + + + + + + + + + (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  + + + + + + + + +
+ + diff --git a/backend/src/main/resources/static/logo-white.png b/backend/src/main/resources/static/logo-white.png new file mode 100644 index 0000000..0ae65e0 Binary files /dev/null and b/backend/src/main/resources/static/logo-white.png differ diff --git a/backend/src/main/resources/static/logo.png b/backend/src/main/resources/static/logo.png new file mode 100644 index 0000000..4f88262 Binary files /dev/null and b/backend/src/main/resources/static/logo.png differ diff --git a/backend/src/main/resources/static/screenshots/01_dashboard.png b/backend/src/main/resources/static/screenshots/01_dashboard.png new file mode 100644 index 0000000..6728dfd Binary files /dev/null and b/backend/src/main/resources/static/screenshots/01_dashboard.png differ diff --git a/backend/src/main/resources/static/screenshots/01_home.png b/backend/src/main/resources/static/screenshots/01_home.png new file mode 100644 index 0000000..9dd704f Binary files /dev/null and b/backend/src/main/resources/static/screenshots/01_home.png differ diff --git a/backend/src/main/resources/static/screenshots/01_home_viewport.png b/backend/src/main/resources/static/screenshots/01_home_viewport.png new file mode 100644 index 0000000..64e7d88 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/01_home_viewport.png differ diff --git a/backend/src/main/resources/static/screenshots/02_guardia.png b/backend/src/main/resources/static/screenshots/02_guardia.png new file mode 100644 index 0000000..df6d4c7 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/02_guardia.png differ diff --git a/backend/src/main/resources/static/screenshots/02_guardia_viewport.png b/backend/src/main/resources/static/screenshots/02_guardia_viewport.png new file mode 100644 index 0000000..19b5add Binary files /dev/null and b/backend/src/main/resources/static/screenshots/02_guardia_viewport.png differ diff --git a/backend/src/main/resources/static/screenshots/02_sr_list.png b/backend/src/main/resources/static/screenshots/02_sr_list.png new file mode 100644 index 0000000..6728dfd Binary files /dev/null and b/backend/src/main/resources/static/screenshots/02_sr_list.png differ diff --git a/backend/src/main/resources/static/screenshots/03_company.png b/backend/src/main/resources/static/screenshots/03_company.png new file mode 100644 index 0000000..88e925d Binary files /dev/null and b/backend/src/main/resources/static/screenshots/03_company.png differ diff --git a/backend/src/main/resources/static/screenshots/03_company_viewport.png b/backend/src/main/resources/static/screenshots/03_company_viewport.png new file mode 100644 index 0000000..e64b817 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/03_company_viewport.png differ diff --git a/backend/src/main/resources/static/screenshots/03_si_project.png b/backend/src/main/resources/static/screenshots/03_si_project.png new file mode 100644 index 0000000..6481c7d Binary files /dev/null and b/backend/src/main/resources/static/screenshots/03_si_project.png differ diff --git a/backend/src/main/resources/static/screenshots/04_contact.png b/backend/src/main/resources/static/screenshots/04_contact.png new file mode 100644 index 0000000..6bfc0eb Binary files /dev/null and b/backend/src/main/resources/static/screenshots/04_contact.png differ diff --git a/backend/src/main/resources/static/screenshots/04_contact_viewport.png b/backend/src/main/resources/static/screenshots/04_contact_viewport.png new file mode 100644 index 0000000..f456b4e Binary files /dev/null and b/backend/src/main/resources/static/screenshots/04_contact_viewport.png differ diff --git a/backend/src/main/resources/static/screenshots/04_incidents.png b/backend/src/main/resources/static/screenshots/04_incidents.png new file mode 100644 index 0000000..a50691c Binary files /dev/null and b/backend/src/main/resources/static/screenshots/04_incidents.png differ diff --git a/backend/src/main/resources/static/screenshots/05_agents.png b/backend/src/main/resources/static/screenshots/05_agents.png new file mode 100644 index 0000000..847ccb8 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/05_agents.png differ diff --git a/backend/src/main/resources/static/screenshots/05_news.png b/backend/src/main/resources/static/screenshots/05_news.png new file mode 100644 index 0000000..48dc214 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/05_news.png differ diff --git a/backend/src/main/resources/static/screenshots/05_news_viewport.png b/backend/src/main/resources/static/screenshots/05_news_viewport.png new file mode 100644 index 0000000..f212eb2 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/05_news_viewport.png differ diff --git a/backend/src/main/resources/static/screenshots/06_license.png b/backend/src/main/resources/static/screenshots/06_license.png new file mode 100644 index 0000000..52f9119 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/06_license.png differ diff --git a/backend/src/main/resources/static/screenshots/06_mobile_home.png b/backend/src/main/resources/static/screenshots/06_mobile_home.png new file mode 100644 index 0000000..c3f3355 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/06_mobile_home.png differ diff --git a/backend/src/main/resources/static/screenshots/all-pages-result.json b/backend/src/main/resources/static/screenshots/all-pages-result.json new file mode 100644 index 0000000..49a94b3 --- /dev/null +++ b/backend/src/main/resources/static/screenshots/all-pages-result.json @@ -0,0 +1,212 @@ +[ + { + "page": "ํ™ˆ", + "url": "/", + "http": 200, + "loadMs": 5849, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "AI ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ์ž์œจ ์šด์˜ ํ”Œ๋žซํผ", + "errors": 0, + "ok": true + }, + { + "page": "GUARDiA ITSM", + "url": "/solution/guardia", + "http": 200, + "loadMs": 1292, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "GUARDiA ITSM", + "errors": 0, + "ok": true + }, + { + "page": "์†”๋ฃจ์…˜-ERP", + "url": "/solution/erp", + "http": 200, + "loadMs": 1767, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "ERP ์†”๋ฃจ์…˜", + "errors": 0, + "ok": true + }, + { + "page": "์†”๋ฃจ์…˜-CRM", + "url": "/solution/crm", + "http": 200, + "loadMs": 1139, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "CRM ์†”๋ฃจ์…˜", + "errors": 0, + "ok": true + }, + { + "page": "์†”๋ฃจ์…˜-BI", + "url": "/solution/bi", + "http": 200, + "loadMs": 966, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "BI ์†”๋ฃจ์…˜", + "errors": 0, + "ok": true + }, + { + "page": "ํšŒ์‚ฌ-CEO์ธ์‚ฌ๋ง", + "url": "/company/greeting", + "http": 200, + "loadMs": 1098, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "CEO ์ธ์‚ฌ๋ง", + "errors": 0, + "ok": true + }, + { + "page": "ํšŒ์‚ฌ-์—ฐํ˜", + "url": "/company/history", + "http": 200, + "loadMs": 1548, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์—ฐํ˜", + "errors": 0, + "ok": true + }, + { + "page": "ํšŒ์‚ฌ-์กฐ์ง๋„", + "url": "/company/organization", + "http": 200, + "loadMs": 892, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์กฐ์ง๋„", + "errors": 0, + "ok": true + }, + { + "page": "ํšŒ์‚ฌ-CI์†Œ๊ฐœ", + "url": "/company/ci", + "http": 200, + "loadMs": 1007, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "CI ์†Œ๊ฐœ", + "errors": 0, + "ok": true + }, + { + "page": "ํšŒ์‚ฌ-์˜ค์‹œ๋Š”๊ธธ", + "url": "/company/location", + "http": 200, + "loadMs": 1070, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์˜ค์‹œ๋Š” ๊ธธ", + "errors": 0, + "ok": true + }, + { + "page": "์‚ฌ์—…-๋ ˆํผ๋Ÿฐ์Šค", + "url": "/business/reference", + "http": 200, + "loadMs": 1111, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "๊ตฌ์ถ• ๋ ˆํผ๋Ÿฐ์Šค", + "errors": 0, + "ok": true + }, + { + "page": "์‚ฌ์—…-ํŒŒํŠธ๋„ˆ", + "url": "/business/partner", + "http": 200, + "loadMs": 1090, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "ํŒŒํŠธ๋„ˆ", + "errors": 0, + "ok": true + }, + { + "page": "์ง€์›-๊ณต์ง€์‚ฌํ•ญ", + "url": "/support/notice", + "http": 200, + "loadMs": 949, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "๊ณต์ง€์‚ฌํ•ญ", + "errors": 0, + "ok": true + }, + { + "page": "์ง€์›-FAQ", + "url": "/support/faq", + "http": 200, + "loadMs": 931, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ", + "errors": 0, + "ok": true + }, + { + "page": "์ง€์›-์นดํƒˆ๋กœ๊ทธ", + "url": "/support/catalog", + "http": 200, + "loadMs": 963, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์นดํƒˆ๋กœ๊ทธ", + "errors": 0, + "ok": true + }, + { + "page": "์ง€์›-๋ฌธ์˜ํ•˜๊ธฐ", + "url": "/support/contact", + "http": 200, + "loadMs": 1007, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "๋ฌธ์˜ํ•˜๊ธฐ", + "errors": 0, + "ok": true + }, + { + "page": "์ฑ„์šฉ-๊ณต๊ณ ", + "url": "/recruit/jobs", + "http": 200, + "loadMs": 984, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์ฑ„์šฉ๊ณต๊ณ ", + "errors": 0, + "ok": true + }, + { + "page": "์ฑ„์šฉ-๋ณต๋ฆฌํ›„์ƒ", + "url": "/recruit/welfare", + "http": 200, + "loadMs": 1275, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "๋ณต๋ฆฌํ›„์ƒ", + "errors": 0, + "ok": true + }, + { + "page": "์ฑ„์šฉ-์ง€์›ํ•˜๊ธฐ", + "url": "/recruit/apply", + "http": 200, + "loadMs": 880, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์ง€์›ํ•˜๊ธฐ", + "errors": 0, + "ok": true + }, + { + "page": "๋‰ด์Šค-๋‰ด์Šค๋ฃธ", + "url": "/news/newsroom", + "http": 200, + "loadMs": 1144, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "๋‰ด์Šค๋ฃธ", + "errors": 0, + "ok": true + }, + { + "page": "๋‰ด์Šค-๋ธ”๋กœ๊ทธ", + "url": "/news/blog", + "http": 200, + "loadMs": 989, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ", + "errors": 0, + "ok": true + } +] \ No newline at end of file diff --git a/backend/src/main/resources/static/screenshots/business_partner.png b/backend/src/main/resources/static/screenshots/business_partner.png new file mode 100644 index 0000000..3dce8a2 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/business_partner.png differ diff --git a/backend/src/main/resources/static/screenshots/business_ref.png b/backend/src/main/resources/static/screenshots/business_ref.png new file mode 100644 index 0000000..8b93a0f Binary files /dev/null and b/backend/src/main/resources/static/screenshots/business_ref.png differ diff --git a/backend/src/main/resources/static/screenshots/company_ci.png b/backend/src/main/resources/static/screenshots/company_ci.png new file mode 100644 index 0000000..2eb6f3c Binary files /dev/null and b/backend/src/main/resources/static/screenshots/company_ci.png differ diff --git a/backend/src/main/resources/static/screenshots/company_greeting.png b/backend/src/main/resources/static/screenshots/company_greeting.png new file mode 100644 index 0000000..9f93270 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/company_greeting.png differ diff --git a/backend/src/main/resources/static/screenshots/company_history.png b/backend/src/main/resources/static/screenshots/company_history.png new file mode 100644 index 0000000..bc970cb Binary files /dev/null and b/backend/src/main/resources/static/screenshots/company_history.png differ diff --git a/backend/src/main/resources/static/screenshots/company_location.png b/backend/src/main/resources/static/screenshots/company_location.png new file mode 100644 index 0000000..5132a04 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/company_location.png differ diff --git a/backend/src/main/resources/static/screenshots/company_org.png b/backend/src/main/resources/static/screenshots/company_org.png new file mode 100644 index 0000000..c38e7b0 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/company_org.png differ diff --git a/backend/src/main/resources/static/screenshots/guardia.png b/backend/src/main/resources/static/screenshots/guardia.png new file mode 100644 index 0000000..19b5add Binary files /dev/null and b/backend/src/main/resources/static/screenshots/guardia.png differ diff --git a/backend/src/main/resources/static/screenshots/home.png b/backend/src/main/resources/static/screenshots/home.png new file mode 100644 index 0000000..5b4df9d Binary files /dev/null and b/backend/src/main/resources/static/screenshots/home.png differ diff --git a/backend/src/main/resources/static/screenshots/news_blog.png b/backend/src/main/resources/static/screenshots/news_blog.png new file mode 100644 index 0000000..94697ec Binary files /dev/null and b/backend/src/main/resources/static/screenshots/news_blog.png differ diff --git a/backend/src/main/resources/static/screenshots/news_newsroom.png b/backend/src/main/resources/static/screenshots/news_newsroom.png new file mode 100644 index 0000000..d799d77 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/news_newsroom.png differ diff --git a/backend/src/main/resources/static/screenshots/recruit_apply.png b/backend/src/main/resources/static/screenshots/recruit_apply.png new file mode 100644 index 0000000..8e79f18 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/recruit_apply.png differ diff --git a/backend/src/main/resources/static/screenshots/recruit_jobs.png b/backend/src/main/resources/static/screenshots/recruit_jobs.png new file mode 100644 index 0000000..951e94f Binary files /dev/null and b/backend/src/main/resources/static/screenshots/recruit_jobs.png differ diff --git a/backend/src/main/resources/static/screenshots/recruit_welfare.png b/backend/src/main/resources/static/screenshots/recruit_welfare.png new file mode 100644 index 0000000..53752fb Binary files /dev/null and b/backend/src/main/resources/static/screenshots/recruit_welfare.png differ diff --git a/backend/src/main/resources/static/screenshots/solution_bi.png b/backend/src/main/resources/static/screenshots/solution_bi.png new file mode 100644 index 0000000..6999f9f Binary files /dev/null and b/backend/src/main/resources/static/screenshots/solution_bi.png differ diff --git a/backend/src/main/resources/static/screenshots/solution_crm.png b/backend/src/main/resources/static/screenshots/solution_crm.png new file mode 100644 index 0000000..a45d34d Binary files /dev/null and b/backend/src/main/resources/static/screenshots/solution_crm.png differ diff --git a/backend/src/main/resources/static/screenshots/solution_erp.png b/backend/src/main/resources/static/screenshots/solution_erp.png new file mode 100644 index 0000000..c647195 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/solution_erp.png differ diff --git a/backend/src/main/resources/static/screenshots/support_catalog.png b/backend/src/main/resources/static/screenshots/support_catalog.png new file mode 100644 index 0000000..e9ee299 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/support_catalog.png differ diff --git a/backend/src/main/resources/static/screenshots/support_contact.png b/backend/src/main/resources/static/screenshots/support_contact.png new file mode 100644 index 0000000..f456b4e Binary files /dev/null and b/backend/src/main/resources/static/screenshots/support_contact.png differ diff --git a/backend/src/main/resources/static/screenshots/support_faq.png b/backend/src/main/resources/static/screenshots/support_faq.png new file mode 100644 index 0000000..62006a5 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/support_faq.png differ diff --git a/backend/src/main/resources/static/screenshots/support_notice.png b/backend/src/main/resources/static/screenshots/support_notice.png new file mode 100644 index 0000000..eba6bd0 Binary files /dev/null and b/backend/src/main/resources/static/screenshots/support_notice.png differ diff --git a/backend/src/main/resources/static/screenshots/test-result.json b/backend/src/main/resources/static/screenshots/test-result.json new file mode 100644 index 0000000..04877fa --- /dev/null +++ b/backend/src/main/resources/static/screenshots/test-result.json @@ -0,0 +1,67 @@ +[ + { + "page": "ํ™ˆ", + "url": "/", + "status": 200, + "loadMs": 9745, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "links": 37, + "images": 2, + "h1": 1, + "errors": 0, + "errorMsgs": [], + "screenshot": "C:\\GUARDiA\\workspace\\zioinfo-web\\frontend\\public\\screenshots\\01_home.png" + }, + { + "page": "GUARDiA ์†Œ๊ฐœ", + "url": "/solution/guardia", + "status": 200, + "loadMs": 1130, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "links": 27, + "images": 8, + "h1": 1, + "errors": 0, + "errorMsgs": [], + "screenshot": "C:\\GUARDiA\\workspace\\zioinfo-web\\frontend\\public\\screenshots\\02_guardia.png" + }, + { + "page": "ํšŒ์‚ฌ์†Œ๊ฐœ", + "url": "/company/greeting", + "status": 200, + "loadMs": 971, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "links": 23, + "images": 2, + "h1": 1, + "errors": 0, + "errorMsgs": [], + "screenshot": "C:\\GUARDiA\\workspace\\zioinfo-web\\frontend\\public\\screenshots\\03_company.png" + }, + { + "page": "๋ฌธ์˜ํ•˜๊ธฐ", + "url": "/support/contact", + "status": 200, + "loadMs": 890, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "links": 24, + "images": 2, + "h1": 1, + "errors": 0, + "errorMsgs": [], + "screenshot": "C:\\GUARDiA\\workspace\\zioinfo-web\\frontend\\public\\screenshots\\04_contact.png" + }, + { + "page": "๋‰ด์Šค", + "url": "/news/press", + "status": 200, + "loadMs": 1007, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "links": 23, + "images": 2, + "h1": 1, + "errors": 0, + "errorMsgs": [], + "screenshot": "C:\\GUARDiA\\workspace\\zioinfo-web\\frontend\\public\\screenshots\\05_news.png" + } +] \ No newline at end of file diff --git a/backend/target/classes/application.yml b/backend/target/classes/application.yml index 2187e8f..40e7a4c 100644 --- a/backend/target/classes/application.yml +++ b/backend/target/classes/application.yml @@ -32,6 +32,9 @@ spring: mail.smtp.starttls.enable: true zioinfo: + jwt: + secret: zioinfo-admin-jwt-secret-key-must-be-at-least-32-chars-long + expiration-ms: 28800000 # 8์‹œ๊ฐ„ company: name: (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  email: info@zioinfo.co.kr diff --git a/backend/target/classes/kr/co/zioinfo/web/config/DataInitializer.class b/backend/target/classes/kr/co/zioinfo/web/config/DataInitializer.class index f390537..3c69d9a 100644 Binary files a/backend/target/classes/kr/co/zioinfo/web/config/DataInitializer.class and b/backend/target/classes/kr/co/zioinfo/web/config/DataInitializer.class differ diff --git a/backend/target/classes/kr/co/zioinfo/web/controller/ApiController.class b/backend/target/classes/kr/co/zioinfo/web/controller/ApiController.class index 12d8594..b0755fe 100644 Binary files a/backend/target/classes/kr/co/zioinfo/web/controller/ApiController.class and b/backend/target/classes/kr/co/zioinfo/web/controller/ApiController.class differ diff --git a/backend/target/classes/kr/co/zioinfo/web/repository/InquiryRepository.class b/backend/target/classes/kr/co/zioinfo/web/repository/InquiryRepository.class index 39e5268..97c6337 100644 Binary files a/backend/target/classes/kr/co/zioinfo/web/repository/InquiryRepository.class and b/backend/target/classes/kr/co/zioinfo/web/repository/InquiryRepository.class differ diff --git a/backend/target/classes/kr/co/zioinfo/web/repository/NewsRepository.class b/backend/target/classes/kr/co/zioinfo/web/repository/NewsRepository.class index 73a5087..09bff3e 100644 Binary files a/backend/target/classes/kr/co/zioinfo/web/repository/NewsRepository.class and b/backend/target/classes/kr/co/zioinfo/web/repository/NewsRepository.class differ diff --git a/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst index 8942c21..f2dc0e9 100644 --- a/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ b/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -1,11 +1,11 @@ kr\co\zioinfo\web\model\Inquiry.class -kr\co\zioinfo\web\controller\ApiController.class kr\co\zioinfo\web\model\Inquiry$InquiryBuilder.class kr\co\zioinfo\web\config\DataInitializer.class -kr\co\zioinfo\web\model\News$NewsBuilder.class -kr\co\zioinfo\web\repository\NewsRepository.class kr\co\zioinfo\web\service\InquiryService.class -kr\co\zioinfo\web\service\NewsService.class -kr\co\zioinfo\web\model\News.class kr\co\zioinfo\web\repository\InquiryRepository.class kr\co\zioinfo\web\ZioinfoWebApplication.class +kr\co\zioinfo\web\controller\ApiController.class +kr\co\zioinfo\web\model\News$NewsBuilder.class +kr\co\zioinfo\web\repository\NewsRepository.class +kr\co\zioinfo\web\service\NewsService.class +kr\co\zioinfo\web\model\News.class diff --git a/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst index 2af0587..26a77bf 100644 --- a/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ b/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -1,9 +1,17 @@ -C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\model\News.java -C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\config\DataInitializer.java +C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\config\SecurityConfig.java C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\service\NewsService.java +C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\controller\AdminController.java C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\ZioinfoWebApplication.java C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\repository\InquiryRepository.java C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\service\InquiryService.java C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\controller\ApiController.java -C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\repository\NewsRepository.java +C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\repository\AdminUserRepository.java C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\model\Inquiry.java +C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\model\News.java +C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\config\DataInitializer.java +C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\model\Recruit.java +C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\repository\RecruitRepository.java +C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\security\JwtUtil.java +C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\repository\NewsRepository.java +C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\model\AdminUser.java +C:\GUARDiA\workspace\zioinfo-web\backend\src\main\java\kr\co\zioinfo\web\security\JwtAuthFilter.java diff --git a/deploy/01_oracle_cloud_guide.md b/deploy/01_oracle_cloud_guide.md new file mode 100644 index 0000000..825a4a8 --- /dev/null +++ b/deploy/01_oracle_cloud_guide.md @@ -0,0 +1,69 @@ +# Oracle Cloud Always Free โ€” zio-server ๊ตฌ์ถ• ๊ฐ€์ด๋“œ + +## 1๋‹จ๊ณ„: Oracle Cloud ๊ณ„์ • ์ƒ์„ฑ + +1. https://www.oracle.com/cloud/free/ ์ ‘์† +2. "Start for free" ํด๋ฆญ +3. ์ •๋ณด ์ž…๋ ฅ: + - Country: South Korea + - ์ด๋ฆ„, ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ +4. **์‹ ์šฉ์นด๋“œ ๋“ฑ๋ก ํ•„์ˆ˜** (๊ณผ๊ธˆ ์—†์Œ โ€” ์ธ์ฆ์šฉ) +5. ๊ฐ€์ž… ์™„๋ฃŒ ํ›„ ํ™ˆ ๋ฆฌ์ „ ์„ ํƒ: **South Korea Central (Seoul)** + +> โš ๏ธ ํ™ˆ ๋ฆฌ์ „์€ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€ โ€” ๋ฐ˜๋“œ์‹œ Seoul ์„ ํƒ + +--- + +## 2๋‹จ๊ณ„: VM ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ (zio-server) + +### ์ฝ˜์†” ์ ‘์† +Oracle Cloud Console โ†’ Compute โ†’ Instances โ†’ Create Instance + +### ์„ค์ •๊ฐ’ + +| ํ•ญ๋ชฉ | ๊ฐ’ | +|------|----| +| **Name** | `zio-server` | +| **Image** | Ubuntu 22.04 (Canonical) | +| **Shape** | VM.Standard.A1.Flex (Ampere) | +| **OCPU** | 4 | +| **Memory** | 24 GB | +| **Boot Volume** | 100 GB | +| **Network** | Default VCN, Public Subnet | +| **๊ณต์ธ IP** | Assign public IP: Yes | + +### SSH ํ‚ค ์ƒ์„ฑ +``` +๋กœ์ปฌ์—์„œ: +ssh-keygen -t rsa -b 4096 -f C:\Users\{username}\.ssh\zio-server +``` +- ์ƒ์„ฑ๋œ `zio-server.pub` ๋‚ด์šฉ์„ ์ฝ˜์†”์— ๋ถ™์—ฌ๋„ฃ๊ธฐ + +### ์ƒ์„ฑ ์™„๋ฃŒ +- ์•ฝ 2~3๋ถ„ ํ›„ Running ์ƒํƒœ ํ™•์ธ +- ๊ณต์ธ IP ๋ฉ”๋ชจ (์˜ˆ: 140.238.xxx.xxx) + +--- + +## 3๋‹จ๊ณ„: ๋ฐฉํ™”๋ฒฝ ์˜คํ”ˆ (Security List) + +Networking โ†’ Virtual Cloud Networks โ†’ Default VCN +โ†’ Security Lists โ†’ Default Security List +โ†’ Add Ingress Rules: + +| ํฌํŠธ | ํ”„๋กœํ† ์ฝœ | ์šฉ๋„ | +|------|---------|------| +| 22 | TCP | SSH | +| 80 | TCP | HTTP | +| 443 | TCP | HTTPS | +| 8080 | TCP | Spring Boot (๊ฐœ๋ฐœ์šฉ) | + +--- + +## 4๋‹จ๊ณ„: SSH ์ ‘์† + +```powershell +ssh -i C:\Users\{username}\.ssh\zio-server ubuntu@{๊ณต์ธIP} +``` + +์ ‘์† ์„ฑ๊ณต ํ›„ โ†’ 5๋‹จ๊ณ„ ์„œ๋ฒ„ ์„ค์ • ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ diff --git a/deploy/02_server_setup.sh b/deploy/02_server_setup.sh new file mode 100644 index 0000000..876b62a --- /dev/null +++ b/deploy/02_server_setup.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# ============================================================ +# zio-server ์ดˆ๊ธฐ ํ™˜๊ฒฝ ๊ตฌ์„ฑ ์Šคํฌ๋ฆฝํŠธ +# Oracle Cloud Ubuntu 22.04 ARM (Ampere A1) +# ์‹คํ–‰: bash 02_server_setup.sh +# ============================================================ + +set -e +GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' +info() { echo -e "${GREEN}[OK]${NC} $1"; } +section() { echo -e "\n${CYAN}=== $1 ===${NC}"; } + +section "1. ์‹œ์Šคํ…œ ์—…๋ฐ์ดํŠธ" +sudo apt-get update -y && sudo apt-get upgrade -y +sudo apt-get install -y curl wget git unzip net-tools ufw htop +info "์‹œ์Šคํ…œ ์—…๋ฐ์ดํŠธ ์™„๋ฃŒ" + +section "2. Java 21 ์„ค์น˜ (Spring Boot์šฉ)" +sudo apt-get install -y openjdk-21-jdk +java -version +info "Java 21 ์„ค์น˜ ์™„๋ฃŒ" + +section "3. Node.js 20 LTS ์„ค์น˜ (React ๋นŒ๋“œ์šฉ)" +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +sudo apt-get install -y nodejs +node -v && npm -v +info "Node.js $(node -v) ์„ค์น˜ ์™„๋ฃŒ" + +section "4. Nginx ์„ค์น˜" +sudo apt-get install -y nginx +sudo systemctl enable nginx +sudo systemctl start nginx +info "Nginx ์„ค์น˜ ์™„๋ฃŒ" + +section "5. UFW ๋ฐฉํ™”๋ฒฝ ์„ค์ •" +sudo ufw allow ssh +sudo ufw allow 80/tcp +sudo ufw allow 443/tcp +sudo ufw allow 8080/tcp +sudo ufw --force enable +sudo ufw status +info "๋ฐฉํ™”๋ฒฝ ์„ค์ • ์™„๋ฃŒ" + +# Oracle Cloud ๋‚ด๋ถ€ iptables๋„ ์—ด๊ธฐ (ํ•„์ˆ˜!) +section "6. Oracle Cloud iptables ๊ทœ์น™ ์ถ”๊ฐ€" +sudo iptables -I INPUT 6 -m state --state NEW -p tcp --dport 80 -j ACCEPT +sudo iptables -I INPUT 6 -m state --state NEW -p tcp --dport 443 -j ACCEPT +sudo iptables -I INPUT 6 -m state --state NEW -p tcp --dport 8080 -j ACCEPT +sudo netfilter-persistent save 2>/dev/null || { + sudo apt-get install -y iptables-persistent + sudo netfilter-persistent save +} +info "iptables ๊ทœ์น™ ์ €์žฅ ์™„๋ฃŒ" + +section "7. ์•ฑ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ" +sudo mkdir -p /var/www/zioinfo +sudo mkdir -p /opt/zioinfo/app +sudo chown -R ubuntu:ubuntu /var/www/zioinfo /opt/zioinfo +info "๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ ์™„๋ฃŒ" + +section "8. Nginx ์„ค์ •" +sudo tee /etc/nginx/sites-available/zioinfo > /dev/null <<'NGINX' +server { + listen 80; + server_name _; + + root /var/www/zioinfo; + index index.html; + + # React SPA โ€” ๋ชจ๋“  ๊ฒฝ๋กœ๋ฅผ index.html๋กœ + location / { + try_files $uri $uri/ /index.html; + } + + # Spring Boot API ํ”„๋ก์‹œ + location /api/ { + proxy_pass http://localhost:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_read_timeout 60s; + } + + # ์ •์  ํŒŒ์ผ ์บ์‹œ + location ~* \.(js|css|png|jpg|gif|ico|svg|woff2)$ { + expires 30d; + add_header Cache-Control "public, immutable"; + } + + # ๋ณด์•ˆ ํ—ค๋” + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-Content-Type-Options "nosniff"; + add_header X-XSS-Protection "1; mode=block"; + + # Gzip ์••์ถ• + gzip on; + gzip_types text/plain text/css application/javascript application/json image/svg+xml; + gzip_min_length 1024; +} +NGINX + +sudo ln -sf /etc/nginx/sites-available/zioinfo /etc/nginx/sites-enabled/ +sudo rm -f /etc/nginx/sites-enabled/default +sudo nginx -t && sudo systemctl reload nginx +info "Nginx ์„ค์ • ์™„๋ฃŒ" + +section "โœ… ์„œ๋ฒ„ ์ดˆ๊ธฐ ๊ตฌ์„ฑ ์™„๋ฃŒ!" +echo "" +echo -e "${YELLOW}๋‹ค์Œ ๋‹จ๊ณ„: ๋กœ์ปฌ์—์„œ 03_deploy.sh ์‹คํ–‰${NC}" +echo -e "์„œ๋ฒ„ IP: $(curl -s ifconfig.me)" diff --git a/deploy/03_deploy.ps1 b/deploy/03_deploy.ps1 new file mode 100644 index 0000000..a5f6374 --- /dev/null +++ b/deploy/03_deploy.ps1 @@ -0,0 +1,110 @@ +# ============================================================ +# zio-server ํ™ˆํŽ˜์ด์ง€ ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ (Windows PowerShell) +# ์‹คํ–‰: .\deploy\03_deploy.ps1 -ServerIP "140.238.xxx.xxx" +# ============================================================ + +param( + [Parameter(Mandatory=$true)] + [string]$ServerIP, + [string]$KeyPath = "$env:USERPROFILE\.ssh\zio-server", + [string]$User = "ubuntu" +) + +$GREEN = "`e[32m" +$YELLOW = "`e[33m" +$CYAN = "`e[36m" +$NC = "`e[0m" + +function Log-Info { param($msg) Write-Host "${GREEN}[OK]${NC} $msg" } +function Log-Section { param($msg) Write-Host "`n${CYAN}=== $msg ===${NC}" } +function Log-Warn { param($msg) Write-Host "${YELLOW}[!]${NC} $msg" } + +$SSH = "ssh -i `"$KeyPath`" -o StrictHostKeyChecking=no ${User}@${ServerIP}" +$SCP = "scp -i `"$KeyPath`" -o StrictHostKeyChecking=no" +$ROOT = "C:\GUARDiA\workspace\zioinfo-web" + +Log-Section "1. React ํ”„๋ก ํŠธ์—”๋“œ ๋นŒ๋“œ" +Set-Location "$ROOT\frontend" + +# vite.config.js ๋นŒ๋“œ ๊ฒฝ๋กœ๋ฅผ ์ž„์‹œ dist๋กœ ๋ณ€๊ฒฝ +$viteCfg = Get-Content "vite.config.js" -Raw +$buildCfg = $viteCfg -replace "outDir: '.*?'", "outDir: 'dist'" +$buildCfg | Set-Content "vite.config.js" -Encoding utf8 + +npm run build +if ($LASTEXITCODE -ne 0) { Write-Error "๋นŒ๋“œ ์‹คํŒจ"; exit 1 } +Log-Info "React ๋นŒ๋“œ ์™„๋ฃŒ โ†’ frontend/dist/" + +Log-Section "2. ๋นŒ๋“œ ํŒŒ์ผ ์„œ๋ฒ„ ์—…๋กœ๋“œ" +Invoke-Expression "$SSH 'rm -rf /var/www/zioinfo/* && mkdir -p /var/www/zioinfo'" +Invoke-Expression "$SCP -r `"$ROOT\frontend\dist\*`" ${User}@${ServerIP}:/var/www/zioinfo/" +Log-Info "์ •์  ํŒŒ์ผ ์—…๋กœ๋“œ ์™„๋ฃŒ" + +Log-Section "3. Spring Boot JAR ๋นŒ๋“œ" +Set-Location "$ROOT" +if (Test-Path "pom.xml") { + # Maven ๋นŒ๋“œ (Spring Boot ๋ฐฑ์—”๋“œ) + $mvnw = if (Test-Path "mvnw.cmd") { ".\mvnw.cmd" } else { "mvn" } + & $mvnw clean package -DskipTests -q + $jar = Get-ChildItem "target\*.jar" -Exclude "*sources*" | Select-Object -First 1 + if ($jar) { + Log-Info "JAR ๋นŒ๋“œ ์™„๋ฃŒ: $($jar.Name)" + Invoke-Expression "$SCP `"$($jar.FullName)`" ${User}@${ServerIP}:/opt/zioinfo/app/zioinfo.jar" + Log-Info "JAR ์—…๋กœ๋“œ ์™„๋ฃŒ" + } +} else { + Log-Warn "pom.xml ์—†์Œ โ€” Spring Boot ๋ฐฐํฌ ์Šคํ‚ต (์ •์  ํŒŒ์ผ๋งŒ ๋ฐฐํฌ)" +} + +Log-Section "4. systemd ์„œ๋น„์Šค ๋“ฑ๋ก (Spring Boot)" +$serviceScript = @' +# Spring Boot ์„œ๋น„์Šค ์„ค์ • +sudo tee /etc/systemd/system/zioinfo.service > /dev/null </dev/null || echo "์„œ๋น„์Šค ์‹œ์ž‘ ๋Œ€๊ธฐ ์ค‘..." +'@ + +if (Test-Path "$ROOT\target\*.jar") { + Invoke-Expression "$SSH '$serviceScript'" + Log-Info "Spring Boot ์„œ๋น„์Šค ๋“ฑ๋ก ์™„๋ฃŒ" +} + +Log-Section "5. Nginx ์žฌ์‹œ์ž‘ ๋ฐ ์ตœ์ข… ํ™•์ธ" +$checkScript = @" +sudo systemctl reload nginx +echo '--- Nginx ์ƒํƒœ ---' +sudo systemctl is-active nginx +echo '--- ํฌํŠธ ํ™•์ธ ---' +ss -tlnp | grep -E ':80|:443|:8080' +echo '--- ๋””์Šคํฌ ์‚ฌ์šฉ๋Ÿ‰ ---' +df -h / +echo '--- ๋ฉ”๋ชจ๋ฆฌ ---' +free -h +"@ +Invoke-Expression "$SSH '$checkScript'" + +Log-Section "โœ… ๋ฐฐํฌ ์™„๋ฃŒ!" +Write-Host "" +Write-Host "${GREEN}ํ™ˆํŽ˜์ด์ง€ ์ฃผ์†Œ:${NC} http://$ServerIP" +Write-Host "${GREEN}SSH ์ ‘์†:${NC} ssh -i `"$KeyPath`" ubuntu@$ServerIP" +Write-Host "" +Write-Host "${YELLOW}๋ธŒ๋ผ์šฐ์ €์—์„œ ํ™•์ธ:${NC}" +Start-Process "http://$ServerIP" diff --git a/deploy/04_ssl_setup.sh b/deploy/04_ssl_setup.sh new file mode 100644 index 0000000..ffd3cea --- /dev/null +++ b/deploy/04_ssl_setup.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# ============================================================ +# SSL ์ธ์ฆ์„œ ์„ค์ • (Let's Encrypt / Certbot) +# ๋„๋ฉ”์ธ์ด ์žˆ์„ ๊ฒฝ์šฐ ์‹คํ–‰ +# ์‹คํ–‰: bash 04_ssl_setup.sh yourdomain.com +# ============================================================ + +DOMAIN=${1:-"zioinfo.co.kr"} + +echo "[1] Certbot ์„ค์น˜" +sudo apt-get install -y certbot python3-certbot-nginx + +echo "[2] SSL ์ธ์ฆ์„œ ๋ฐœ๊ธ‰ โ€” $DOMAIN" +sudo certbot --nginx -d $DOMAIN -d www.$DOMAIN \ + --non-interactive --agree-tos --email admin@$DOMAIN \ + --redirect + +echo "[3] ์ž๋™ ๊ฐฑ์‹  ํ™•์ธ" +sudo certbot renew --dry-run + +echo "[4] Nginx ์žฌ์‹œ์ž‘" +sudo systemctl reload nginx + +echo "โœ… SSL ์„ค์ • ์™„๋ฃŒ!" +echo " https://$DOMAIN ์œผ๋กœ ์ ‘์†ํ•˜์„ธ์š”" diff --git a/deploy/05_update_deploy.sh b/deploy/05_update_deploy.sh new file mode 100644 index 0000000..8a0138f --- /dev/null +++ b/deploy/05_update_deploy.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# ============================================================ +# ๋น ๋ฅธ ์—…๋ฐ์ดํŠธ ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ (์„œ๋ฒ„์—์„œ ์‹คํ–‰) +# ๋กœ์ปฌ์—์„œ SCP๋กœ ํŒŒ์ผ ์˜ฌ๋ฆฐ ํ›„ ์„œ๋ฒ„์—์„œ ์‹คํ–‰ +# ์‹คํ–‰: bash 05_update_deploy.sh +# ============================================================ + +echo "[1] ์ƒˆ ํŒŒ์ผ ์ ์šฉ" +sudo cp -r /tmp/dist/* /var/www/zioinfo/ +sudo chown -R www-data:www-data /var/www/zioinfo/ + +echo "[2] Spring Boot ์žฌ์‹œ์ž‘ (์žˆ์„ ๊ฒฝ์šฐ)" +if systemctl is-active --quiet zioinfo; then + sudo systemctl restart zioinfo + echo " Spring Boot ์žฌ์‹œ์ž‘๋จ" +fi + +echo "[3] Nginx ์žฌ๋กœ๋“œ" +sudo nginx -t && sudo systemctl reload nginx + +echo "โœ… ์—…๋ฐ์ดํŠธ ์™„๋ฃŒ! $(date)" diff --git a/deploy/06_guardia_server_setup.sh b/deploy/06_guardia_server_setup.sh new file mode 100644 index 0000000..d7f920d --- /dev/null +++ b/deploy/06_guardia_server_setup.sh @@ -0,0 +1,135 @@ +#!/bin/bash +# ============================================================ +# GUARDiA ITSM ์„œ๋ฒ„ ํ™˜๊ฒฝ ๊ตฌ์„ฑ +# Oracle Cloud Ubuntu 22.04 ARM (Ampere A1) +# ์‹คํ–‰: bash 06_guardia_server_setup.sh +# ============================================================ + +set -e +GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' +info() { echo -e "${GREEN}[OK]${NC} $1"; } +warn() { echo -e "${YELLOW}[!]${NC} $1"; } +section() { echo -e "\n${CYAN}โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}"; echo -e "${CYAN} $1${NC}"; echo -e "${CYAN}โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}"; } + +# โ”€โ”€ 1. Python 3.11 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +section "1. Python 3.11 ์„ค์น˜" +sudo apt-get install -y python3.11 python3.11-venv python3.11-dev python3-pip +sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1 +python3 --version +info "Python 3.11 ์„ค์น˜ ์™„๋ฃŒ" + +# โ”€โ”€ 2. PostgreSQL 15 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +section "2. PostgreSQL 15 ์„ค์น˜" +sudo apt-get install -y postgresql postgresql-contrib +sudo systemctl enable postgresql +sudo systemctl start postgresql + +# DB / ์‚ฌ์šฉ์ž ์ƒ์„ฑ +sudo -u postgres psql < /opt/guardia/app/.env < /dev/null </dev/null || true +fi + +# DB ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ +python3 db_init.py --force 2>/dev/null || python3 -c " +from database import engine, Base +from models import * +import asyncio +async def init(): + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) +asyncio.run(init()) +print('DB ์ดˆ๊ธฐํ™” ์™„๋ฃŒ') +" 2>/dev/null || echo "DB ์ดˆ๊ธฐํ™” ์Šคํ‚ต (์ด๋ฏธ ์กด์žฌ)" + +echo "[4] ์„œ๋น„์Šค ์žฌ์‹œ์ž‘" +sudo systemctl restart guardia +sleep 3 +sudo systemctl is-active guardia && echo "GUARDiA ์„œ๋น„์Šค ์‹คํ–‰ ์ค‘" || echo "์„œ๋น„์Šค ์‹œ์ž‘ ์‹คํŒจ โ€” ๋กœ๊ทธ ํ™•์ธ ํ•„์š”" + +echo "[5] ์ •๋ฆฌ" +rm -rf /tmp/guardia_src /tmp/guardia_deploy.zip +echo "๋ฐฐํฌ ์™„๋ฃŒ!" +'@ + +Invoke-Expression "$SSH 'bash -s'" | bash -s <<< "$deployScript" + +Log-Section "4. ์ ‘์† ํ™•์ธ" +$checkScript = @" +echo '--- GUARDiA ์„œ๋น„์Šค ์ƒํƒœ ---' +sudo systemctl status guardia --no-pager -l | head -20 +echo '' +echo '--- ํฌํŠธ ํ™•์ธ ---' +ss -tlnp | grep -E ':8001|:8080|:80' +echo '' +echo '--- ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ---' +free -h +echo '' +echo '--- GUARDiA API ํ—ฌ์Šค์ฒดํฌ ---' +sleep 2 +curl -s http://localhost:8001/api/admin/health | python3 -m json.tool 2>/dev/null || echo 'API ์‘๋‹ต ๋Œ€๊ธฐ ์ค‘...' +"@ +Invoke-Expression "$SSH '$checkScript'" + +Write-Host "" +Write-Host "`e[32mโœ… GUARDiA ITSM ๋ฐฐํฌ ์™„๋ฃŒ!`e[0m" +Write-Host "" +Write-Host " ํ™ˆํŽ˜์ด์ง€: http://$ServerIP" +Write-Host " GUARDiA ITSM: http://$ServerIP`:8001" +Write-Host " ๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ: admin / admin (์ตœ์ดˆ ์ ‘์† ํ›„ ๋ณ€๊ฒฝ ํ•„์ˆ˜)" +Write-Host "" +Write-Host "`e[33m๋ณด์•ˆ ๊ถŒ๊ณ :`e[0m" +Write-Host " 1. ์ตœ์ดˆ ๋กœ๊ทธ์ธ ํ›„ ์ฆ‰์‹œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ" +Write-Host " 2. MFA ํ™œ์„ฑํ™”: POST /api/auth/mfa/setup" +Write-Host " 3. ํฌํŠธ 8001 โ†’ Nginx ๋’ค๋กœ ์ˆจ๊ธฐ๊ธฐ (07_nginx_all.sh ์‹คํ–‰)" diff --git a/deploy/09_full_deploy_guide.md b/deploy/09_full_deploy_guide.md new file mode 100644 index 0000000..3cf53dc --- /dev/null +++ b/deploy/09_full_deploy_guide.md @@ -0,0 +1,44 @@ +# zio-server ์ „์ฒด ๋ฐฐํฌ ๊ฐ€์ด๋“œ + +## ์„œ๋ฒ„ ๊ตฌ์„ฑ ์™„๋ฃŒ ํ›„ ์‹คํ–‰ ์ˆœ์„œ + +```bash +# 1. ๊ธฐ๋ณธ ํ™˜๊ฒฝ (Java, Node, Nginx) +bash 02_server_setup.sh + +# 2. GUARDiA ํ™˜๊ฒฝ (Python, PostgreSQL, Ollama) +bash 06_guardia_server_setup.sh + +# 3. Nginx ํ†ตํ•ฉ ์„ค์ • +bash 07_nginx_all.sh zioinfo.co.kr itsm.zioinfo.co.kr + +# 4. ํ™ˆํŽ˜์ด์ง€ ๋ฐฐํฌ (Windows์—์„œ) +.\deploy\03_deploy.ps1 -ServerIP "์„œ๋ฒ„IP" + +# 5. GUARDiA ๋ฐฐํฌ (Windows์—์„œ) +.\deploy\08_deploy_guardia.ps1 -ServerIP "์„œ๋ฒ„IP" + +# 6. SSL ์ธ์ฆ์„œ (๋„๋ฉ”์ธ ์žˆ์„ ๋•Œ) +bash 04_ssl_setup.sh zioinfo.co.kr +``` + +## ์ตœ์ข… ์„œ๋น„์Šค ๋ชฉ๋ก + +| ์„œ๋น„์Šค | ์ฃผ์†Œ | ํฌํŠธ | +|--------|------|------| +| ์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ํ™ˆํŽ˜์ด์ง€ | http://zioinfo.co.kr | 80/443 | +| GUARDiA ITSM | http://itsm.zioinfo.co.kr | 80/443 | +| PostgreSQL | ๋‚ด๋ถ€ ์ „์šฉ | 5432 | +| Ollama LLM | ๋‚ด๋ถ€ ์ „์šฉ | 11434 | + +## ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ ์˜ˆ์ƒ + +| ๊ตฌ์„ฑ ์š”์†Œ | CPU | RAM | +|-----------|-----|-----| +| Nginx | 0.1 OCPU | 50 MB | +| Spring Boot | 0.5 OCPU | 512 MB | +| FastAPI (GUARDiA) | 0.5 OCPU | 300 MB | +| PostgreSQL | 0.3 OCPU | 256 MB | +| Ollama + LLaMA-3 8B | 1.0 OCPU | 6 GB | +| **ํ•ฉ๊ณ„** | **2.4 OCPU** | **~7.1 GB** | +| **์—ฌ์œ ** | **1.6 OCPU** | **~16.9 GB** โœ… | diff --git a/deploy/10_gitea_smtp_setup.sh b/deploy/10_gitea_smtp_setup.sh new file mode 100644 index 0000000..ef2d41f --- /dev/null +++ b/deploy/10_gitea_smtp_setup.sh @@ -0,0 +1,258 @@ +#!/bin/bash +# ============================================================ +# Gitea (Git ์„œ๋ฒ„) + SMTP (Postfix) ์„ค์น˜ ์Šคํฌ๋ฆฝํŠธ +# Oracle Cloud Ubuntu 22.04 ARM (Ampere A1) +# ์‹คํ–‰: bash 10_gitea_smtp_setup.sh [๋„๋ฉ”์ธ] +# ============================================================ + +set -e +DOMAIN=${1:-"$(curl -s ifconfig.me)"} +GITEA_DOMAIN="git.${DOMAIN}" +GREEN='\033[0;32m'; CYAN='\033[0;36m'; YELLOW='\033[1;33m'; NC='\033[0m' +info() { echo -e "${GREEN}[OK]${NC} $1"; } +section() { echo -e "\n${CYAN}โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}"; echo -e "${CYAN} $1${NC}"; echo -e "${CYAN}โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}"; } + +# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +# PART 1: Gitea ์„ค์น˜ +# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +section "1. Gitea ์‚ฌ์šฉ์ž ์ƒ์„ฑ" +sudo adduser --system --shell /bin/bash --gecos 'Git Version Control' \ + --group --disabled-password --home /home/git git 2>/dev/null || true +info "git ์‚ฌ์šฉ์ž ์ค€๋น„ ์™„๋ฃŒ" + +section "2. Gitea ๋ฐ”์ด๋„ˆ๋ฆฌ ๋‹ค์šด๋กœ๋“œ (ARM64)" +GITEA_VER="1.22.3" +sudo mkdir -p /opt/gitea/bin +sudo wget -q "https://dl.gitea.com/gitea/${GITEA_VER}/gitea-${GITEA_VER}-linux-arm64" \ + -O /opt/gitea/bin/gitea +sudo chmod +x /opt/gitea/bin/gitea +sudo ln -sf /opt/gitea/bin/gitea /usr/local/bin/gitea +gitea --version +info "Gitea ${GITEA_VER} ๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ" + +section "3. Gitea ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ" +sudo mkdir -p /var/lib/gitea/{custom,data,log} +sudo mkdir -p /etc/gitea +sudo chown -R git:git /var/lib/gitea /etc/gitea +sudo chmod -R 750 /var/lib/gitea /etc/gitea +info "Gitea ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ ์™„๋ฃŒ" + +section "4. Gitea PostgreSQL DB ์ƒ์„ฑ" +sudo -u postgres psql </dev/null || true +CREATE USER gitea WITH PASSWORD 'G1tea_2026!'; +CREATE DATABASE gitea_db OWNER gitea; +GRANT ALL PRIVILEGES ON DATABASE gitea_db TO gitea; +PSQL +info "Gitea DB ์ƒ์„ฑ ์™„๋ฃŒ" + +section "5. Gitea ์„ค์ • ํŒŒ์ผ ์ƒ์„ฑ" +sudo tee /etc/gitea/app.ini > /dev/null < /dev/null < /dev/null < /dev/null < /dev/null < timeout: + print('[TIMEOUT]') + break + time.sleep(0.2) + while chan.recv_ready(): + sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + rc = chan.recv_exit_status() + print(f'exit={rc}') + return rc + +# 1. ๋””๋ ‰ํ„ฐ๋ฆฌ ์ค€๋น„ +run('๋””๋ ‰ํ„ฐ๋ฆฌ ์ค€๋น„', + 'mkdir -p /opt/zioinfo/app /var/www/zioinfo /var/log/zioinfo && ' + 'chown -R jenkins:jenkins /opt/zioinfo /var/www/zioinfo /var/log/zioinfo && echo ok') + +# 2. JAR ์—…๋กœ๋“œ +print('\n[JAR ์—…๋กœ๋“œ ์ค‘...]') +sftp.put(LOCAL_JAR, '/opt/zioinfo/app/app.jar') +size_mb = os.path.getsize(LOCAL_JAR) // 1024 // 1024 +print(f'JAR ์—…๋กœ๋“œ ์™„๋ฃŒ: {size_mb}MB') + +# 3. ์ •์  ํŒŒ์ผ zip ํ›„ ์—…๋กœ๋“œ +print('\n[์ •์  ํŒŒ์ผ ํŒจํ‚ค์ง• ์ค‘...]') +zip_buf = io.BytesIO() +count = 0 +with zipfile.ZipFile(zip_buf, 'w', zipfile.ZIP_DEFLATED) as zf: + for root, dirs, files in os.walk(LOCAL_STATIC): + rel = os.path.relpath(root, LOCAL_STATIC).replace('\\', '/') + for fname in files: + arc = fname if rel == '.' else f'{rel}/{fname}' + zf.write(os.path.join(root, fname), arc) + count += 1 +zip_buf.seek(0) +with sftp.open('/tmp/static.zip', 'wb') as f: + f.write(zip_buf.read()) +print(f'{count}๊ฐœ ์ •์  ํŒŒ์ผ ์—…๋กœ๋“œ') + +# 4. ์ •์  ํŒŒ์ผ ๋ฐฐํฌ +run('์ •์  ํŒŒ์ผ ๋ฐฐํฌ', + 'cd /var/www/zioinfo && unzip -q -o /tmp/static.zip && echo deployed && ls | head -5') + +# 5. Spring Boot ์„œ๋น„์Šค ์‹œ์ž‘ +run('Spring Boot ๊ธฐ๋™', + 'systemctl restart zioinfo && sleep 8 && systemctl is-active zioinfo && ' + 'journalctl -u zioinfo -n 8 --no-pager') + +# 6. API ํ—ฌ์Šค์ฒดํฌ +run('API ํ—ฌ์Šค์ฒดํฌ', + 'curl -s -o /dev/null -w "HTTP %{http_code}" http://localhost:8080/api/company && echo " OK"') + +sftp.close() +client.close() +print('\n๋ฐฐํฌ ์™„๋ฃŒ!') diff --git a/deploy/fix_https_8443.py b/deploy/fix_https_8443.py new file mode 100644 index 0000000..4ff26a8 --- /dev/null +++ b/deploy/fix_https_8443.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +"""Nginx 8443 HTTPS ์„ค์ • ์ˆ˜์ •""" +import paramiko, time, sys + +HOST = '101.79.17.164'; USER = 'root'; PASS = '1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +def run(label, cmd, timeout=20): + print(f'\n[{label}]') + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd) + start = time.time() + while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time() - start > timeout: break + time.sleep(0.2) + while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + chan.recv_exit_status() + +guardia_https = r"""server { + listen 8443 ssl; + server_name _; + ssl_certificate /etc/ssl/guardia/server.crt; + ssl_certificate_key /etc/ssl/guardia/server.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; + ssl_prefer_server_ciphers off; + client_max_body_size 100M; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options DENY always; + add_header X-Content-Type-Options nosniff always; + add_header X-XSS-Protection "1; mode=block" always; + location / { + proxy_pass http://127.0.0.1:8001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + proxy_read_timeout 300s; + } + location /api/ { + limit_req zone=guardia_api burst=10 nodelay; + proxy_pass http://127.0.0.1:8001; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto https; + proxy_read_timeout 60s; + } + location /ws/ { + proxy_pass http://127.0.0.1:8001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_read_timeout 3600s; + } +} +""" + +with sftp.open('/etc/nginx/sites-available/guardia-https', 'w') as f: + f.write(guardia_https) +sftp.close() + +run('Nginx ์„ค์ • ๊ฒ€์ฆ', 'nginx -t') +run('Nginx ๋ฆฌ๋กœ๋“œ', 'systemctl reload nginx && echo NGINX_OK') +time.sleep(2) +run('HTTPS 8443 ํ…Œ์ŠคํŠธ', 'curl -sk https://localhost:8443/api/external/health -w " HTTP %{http_code}"') +run('CORS ํ…Œ์ŠคํŠธ (HTTPS)', + 'curl -sk -I -X OPTIONS https://localhost:8443/api/external/health ' + '-H "Origin: https://portal.myorg.go.kr" | grep -i access-control') + +client.close() +print('\n์™„๋ฃŒ') diff --git a/deploy/fix_nginx.py b/deploy/fix_nginx.py new file mode 100644 index 0000000..c4ad386 --- /dev/null +++ b/deploy/fix_nginx.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +import paramiko, time, sys + +HOST = '101.79.17.164'; USER = 'root'; PASS = '1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +def run(cmd, timeout=20): + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd) + start = time.time() + while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time() - start > timeout: break + time.sleep(0.2) + while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + chan.recv_exit_status() + +nginx_conf = r"""server { + listen 80 default_server; + server_name _; + root /var/www/zioinfo; + index index.html; + location / { + try_files $uri $uri/ /index.html; + add_header Cache-Control no-cache; + } + location /api/ { + proxy_pass http://127.0.0.1:8082; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 60s; + } + location ~* \.(js|css|png|jpg|gif|ico|svg|woff2|ttf)$ { + expires 30d; + add_header Cache-Control "public, immutable"; + } + gzip on; + gzip_types text/plain text/css application/javascript application/json; +} +""" + +with sftp.open('/etc/nginx/sites-available/zioinfo', 'w') as f: + f.write(nginx_conf) +sftp.close() + +run('nginx -t && systemctl reload nginx && echo NGINX_OK') +run('curl -s -o /dev/null -w "HTTP %{http_code}" http://localhost/api/company && echo " via Nginx OK"') + +client.close() +print('์™„๋ฃŒ') diff --git a/deploy/gitea_messenger.py b/deploy/gitea_messenger.py new file mode 100644 index 0000000..6d38b28 --- /dev/null +++ b/deploy/gitea_messenger.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +"""GUARDiA Messenger Gitea ์ €์žฅ์†Œ ์ƒ์„ฑ + Push""" +import paramiko, time, sys, os, io, zipfile + +HOST = '101.79.17.164' +USER = 'root' +PASS = '1q2w3e!Q' +LOCAL_APP = 'C:/GUARDiA/app' +SEP = chr(92) + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +def run(label, cmd, timeout=60): + print(f'\n[{label}]') + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd) + start = time.time() + while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time()-start > timeout: print('[TIMEOUT]'); break + time.sleep(0.3) + while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + chan.recv_exit_status() + +# --- 1. ์ €์žฅ์†Œ ์ƒ์„ฑ --- +create_py = """ +import urllib.request, json, base64, urllib.error +G = 'http://localhost:3000' +h = {'Content-Type':'application/json','Authorization':'Basic '+base64.b64encode(b'zio:Zio@Admin2026!').decode()} +d = json.dumps({'name':'guardia-messenger','description':'GUARDiA Messenger Mobile App','private':False,'auto_init':False}).encode() +req = urllib.request.Request(G+'/api/v1/user/repos',data=d,method='POST',headers=h) +try: + r=urllib.request.urlopen(req) + print('OK:',json.loads(r.read()).get('full_name')) +except urllib.error.HTTPError as e: + b=e.read(); print('HTTP',e.code,b.decode()[:100]) +""" +with sftp.open('/tmp/cr.py','w') as f: f.write(create_py) +run('์ €์žฅ์†Œ ์ƒ์„ฑ', 'python3 /tmp/cr.py') + +# --- 2. ์†Œ์Šค ํŒจํ‚ค์ง• --- +print('\n[์†Œ์Šค ํŒจํ‚ค์ง•]') +SKIP = {'node_modules','.git','android','ios','__pycache__','.expo'} +buf = io.BytesIO(); n = 0 +with zipfile.ZipFile(buf,'w',zipfile.ZIP_DEFLATED) as zf: + for root,dirs,files in os.walk(LOCAL_APP): + dirs[:] = [d for d in dirs if d not in SKIP] + rel = os.path.relpath(root,LOCAL_APP).replace(SEP,'/') + for f in files: + if f.endswith(('.pyc','.log')): continue + arc = f if rel=='.' else f'{rel}/{f}' + zf.write(os.path.join(root,f),arc); n+=1 +buf.seek(0) +with sftp.open('/tmp/msg.zip','wb') as f: f.write(buf.read()) +print(f' {n}๊ฐœ ํŒŒ์ผ') + +# --- 3. git push (์„œ๋ฒ„ ์Šคํฌ๋ฆฝํŠธ) --- +push_py = """ +import subprocess, os + +os.makedirs('/tmp/msg-push', exist_ok=True) +subprocess.run(['rm','-rf','/tmp/msg-push'], check=False) +os.makedirs('/tmp/msg-push') +subprocess.run(['unzip','-q','/tmp/msg.zip','-d','/tmp/msg-push'], check=True) + +env = {'HOME':'/root','PATH':'/usr/bin:/bin'} +cwd = '/tmp/msg-push' + +for cmd in [ + ['git','init','-q'], + ['git','config','user.email','ci@zioinfo.co.kr'], + ['git','config','user.name','ZioCI'], + ['git','add','-A'], + ['git','commit','-q','-m','feat: GUARDiA Messenger v1.0.0 initial commit'], + ['git','remote','add','origin','http://zio:Zio%40Admin2026%21@localhost:3000/zio/guardia-messenger.git'], + ['git','push','origin','main'], +]: + r = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True) + if r.returncode != 0 and cmd[1] not in ['config','remote']: + print('ERR',cmd[1],r.stderr[:100]) + else: + print('OK',cmd[1]) +""" +with sftp.open('/tmp/push.py','w') as f: f.write(push_py) +run('Git Push', 'python3 /tmp/push.py', 60) + +# --- 4. Webhook --- +wh_py = """ +import urllib.request, json, base64 +G='http://localhost:3000' +h={'Content-Type':'application/json','Authorization':'Basic '+base64.b64encode(b'zio:Zio@Admin2026!').decode()} +d=json.dumps({'type':'gitea','active':True,'config':{'url':'http://localhost:9999/','content_type':'json','secret':'zioinfo-deploy-2026'},'events':['push']}).encode() +req=urllib.request.Request(G+'/api/v1/repos/zio/guardia-messenger/hooks',data=d,method='POST',headers=h) +try: + r=urllib.request.urlopen(req); print('Webhook OK, ID:',json.loads(r.read()).get('id')) +except Exception as e: print('Webhook ์˜ค๋ฅ˜:',e) +""" +with sftp.open('/tmp/wh.py','w') as f: f.write(wh_py) +run('Webhook ๋“ฑ๋ก', 'python3 /tmp/wh.py') + +# --- 5. ์ตœ์ข… ํ™•์ธ --- +check_py = """ +import urllib.request, json, base64 +G='http://localhost:3000' +h={'Authorization':'Basic '+base64.b64encode(b'zio:Zio@Admin2026!').decode()} +r=urllib.request.Request(G+'/api/v1/repos/zio/guardia-messenger',headers=h) +try: + d=json.loads(urllib.request.urlopen(r).read()) + print('์ €์žฅ์†Œ:', d.get('full_name')) + print('URL: ', d.get('html_url')) + print('๋ธŒ๋žœ์น˜:', d.get('default_branch')) +except Exception as e: print('์˜ค๋ฅ˜:',e) + +# ๋ธŒ๋žœ์น˜ ๋ชฉ๋ก +r2=urllib.request.Request(G+'/api/v1/repos/zio/guardia-messenger/branches',headers=h) +try: + branches=json.loads(urllib.request.urlopen(r2).read()) + print('๋ธŒ๋žœ์น˜ ๋ชฉ๋ก:', [b['name'] for b in branches]) +except: pass +""" +with sftp.open('/tmp/check.py','w') as f: f.write(check_py) +sftp.close() +run('์ตœ์ข… ํ™•์ธ', 'python3 /tmp/check.py') + +client.close() diff --git a/deploy/guardia_mail.py b/deploy/guardia_mail.py new file mode 100644 index 0000000..0c05e55 --- /dev/null +++ b/deploy/guardia_mail.py @@ -0,0 +1,74 @@ +import smtplib +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from datetime import datetime + +TO = 'ythong86@gmail.com' +FROM = 'guardia@zioinfo.co.kr' +NOW = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + +text = ( + 'GUARDiA ITSM ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ\n\n' + '๋ฐœ์‹  ์„œ๋ฒ„: mail.zioinfo.co.kr (101.79.17.164)\n' + '๋ฐœ์‹ ์ž: guardia@zioinfo.co.kr\n' + '๋ฐœ์†ก ์‹œ๊ฐ: ' + NOW + ' KST\n' + '์ธ์ฆ: SPF PASS + DKIM ์„œ๋ช…\n\n' + 'zio ์„œ๋ฒ„ Postfix + OpenDKIM์ด ์ •์ƒ ์ž‘๋™ ์ค‘์ž…๋‹ˆ๋‹ค.\n\n' + '(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  | GUARDiA ITSM v2.0\n' + 'http://101.79.17.164:8001\n' +) + +html = ( + '' + '' + '
' + '
' + '

๐Ÿ›ก๏ธ GUARDiA ITSM

' + '

(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  AI ์ธํ”„๋ผ ์ž์œจ ์šด์˜ ํ”Œ๋žซํผ

' + '
' + '
' + '

๐Ÿ“ง ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ

' + '
' + '

โœ… ๋ฐœ์†ก ์„ฑ๊ณต

' + '

SPF + DKIM ์ธ์ฆ์„ ํ†ต๊ณผํ•˜์—ฌ ๋ฐœ์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

' + '
' + '' + '' + '' + '' + '' + '' + '' + '
๋ฐœ์‹  ์„œ๋ฒ„mail.zioinfo.co.kr (101.79.17.164)
๋ฐœ์‹ ์žguardia@zioinfo.co.kr
๋ฐœ์†ก ์‹œ๊ฐ' + NOW + ' KST
SPF ์ธ์ฆโœ… PASS
DKIM ์„œ๋ช…โœ… ์„œ๋ช…๋จ
' + '
' + '

๐Ÿค– GUARDiA ITSM ์•Œ๋ฆผ ๊ธฐ๋Šฅ

' + '
    ' + '
  • SR ์ ‘์ˆ˜/์™„๋ฃŒ ์•Œ๋ฆผ
  • ' + '
  • ์ธ์‹œ๋˜ํŠธ ๊ธด๊ธ‰ ์•Œ๋ฆผ
  • ' + '
  • SLA ์œ„๋ฐ˜ ๊ฒฝ๊ณ 
  • ' + '
  • ๋ฐฐํฌ ์™„๋ฃŒ/์‹คํŒจ ์•Œ๋ฆผ
  • ' + '
  • ๋ผ์ด์„ ์Šค ๋งŒ๋ฃŒ ์•Œ๋ฆผ
  • ' + '
' + '
' + '
' + 'GUARDiA ITSM v2.0 | (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  | guardia@zioinfo.co.kr' + '
' +) + +msg = MIMEMultipart('alternative') +msg['Subject'] = '[GUARDiA] SMTP ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ - ' + NOW +msg['From'] = 'GUARDiA ITSM <' + FROM + '>' +msg['To'] = TO +msg['X-Mailer'] = 'GUARDiA ITSM v2.0' +msg.attach(MIMEText(text, 'plain', 'utf-8')) +msg.attach(MIMEText(html, 'html', 'utf-8')) + +try: + with smtplib.SMTP('localhost', 25, timeout=15) as smtp: + smtp.ehlo('mail.zioinfo.co.kr') + smtp.sendmail(FROM, [TO], msg.as_string()) + print('OK ๋ฐœ์†ก ์™„๋ฃŒ - ythong86@gmail.com ์ˆ˜์‹ ํ•จ์„ ํ™•์ธํ•˜์„ธ์š”!') + print('๋ฐœ์‹ : ' + FROM + ' โ†’ ' + TO) + print('์‹œ๊ฐ: ' + NOW + ' KST') +except Exception as ex: + print('FAIL:', ex) diff --git a/deploy/guardia_mail_v2.py b/deploy/guardia_mail_v2.py new file mode 100644 index 0000000..c1fb107 --- /dev/null +++ b/deploy/guardia_mail_v2.py @@ -0,0 +1,125 @@ +""" +GUARDiA ์ด๋ฉ”์ผ ๋ฐœ์†ก v2 โ€” ์ŠคํŒธ ์ ์ˆ˜ ์ตœ์†Œํ™” +- Reply-To ํ—ค๋” ์ถ”๊ฐ€ +- List-Unsubscribe ํ—ค๋” ์ถ”๊ฐ€ +- ์ ์ ˆํ•œ Message-ID +- ํ…์ŠคํŠธ ๋ณธ๋ฌธ ์ถฉ์‹คํžˆ ์ž‘์„ฑ +- HTML ๊ณผ๋„ํ•œ ์Šคํƒ€์ผ ์ œ๊ฑฐ +""" +import smtplib, socket +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.utils import formatdate, make_msgid +from datetime import datetime + +TO = 'ythong86@gmail.com' +FROM = 'guardia@zioinfo.co.kr' +NOW = datetime.now().strftime('%Y-%m-%d %H:%M:%S') +DOMAIN = 'zioinfo.co.kr' + +text = ( + 'GUARDiA ITSM ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ\n\n' + '์•ˆ๋…•ํ•˜์„ธ์š”,\n\n' + '(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  GUARDiA ITSM ์‹œ์Šคํ…œ์—์„œ ๋ฐœ์†กํ•œ ํ…Œ์ŠคํŠธ ๋ฉ”์ผ์ž…๋‹ˆ๋‹ค.\n\n' + '[๋ฐœ์†ก ์ •๋ณด]\n' + '- ๋ฐœ์‹ ์ž: guardia@zioinfo.co.kr\n' + '- ๋ฐœ์‹  ์„œ๋ฒ„: mail.zioinfo.co.kr (101.79.17.164)\n' + '- ๋ฐœ์†ก ์‹œ๊ฐ: ' + NOW + ' KST\n' + '- ์ธ์ฆ: SPF PASS + DKIM ์„œ๋ช…\n\n' + '[GUARDiA ITSM ์ด๋ฉ”์ผ ์•Œ๋ฆผ ๊ธฐ๋Šฅ]\n' + '- SR(์„œ๋น„์Šค ์š”์ฒญ) ์ ‘์ˆ˜ ๋ฐ ์™„๋ฃŒ ์•Œ๋ฆผ\n' + '- ์ธ์‹œ๋˜ํŠธ ๋ฐœ์ƒ ๊ธด๊ธ‰ ์•Œ๋ฆผ\n' + '- SLA ์œ„๋ฐ˜ ๊ฒฝ๊ณ  ์•Œ๋ฆผ\n' + '- ๋ฐฐํฌ ์™„๋ฃŒ/์‹คํŒจ ์•Œ๋ฆผ\n' + '- ๋ผ์ด์„ ์Šค ๋งŒ๋ฃŒ ์•Œ๋ฆผ\n\n' + '์ด ๋ฉ”์ผ์€ GUARDiA ITSM ์‹œ์Šคํ…œ ํ…Œ์ŠคํŠธ ๋ชฉ์ ์œผ๋กœ ๋ฐœ์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.\n\n' + '--\n' + '(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ \n' + 'GUARDiA ITSM v2.0\n' + 'guardia@zioinfo.co.kr\n' + 'http://101.79.17.164:8001\n' +) + +html = ( + '\n' + '\n' + '\n' + ' \n' + ' \n' + ' GUARDiA ITSM\n' + '\n' + '\n' + '\n' + '\n' + '
\n' + '\n' + + '\n' + '\n' + + '\n' + '\n' + + '\n' + '\n' + '
\n' + '

GUARDiA ITSM

\n' + '

(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  AI ์ธํ”„๋ผ ์ž์œจ ์šด์˜ ํ”Œ๋žซํผ

\n' + '
\n' + '

์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ

\n' + '

์•ˆ๋…•ํ•˜์„ธ์š”,
\n' + ' (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  GUARDiA ITSM ์‹œ์Šคํ…œ ํ…Œ์ŠคํŠธ ๋ฉ”์ผ์ž…๋‹ˆ๋‹ค.

\n' + + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + '
๋ฐœ์‹ ์žguardia@zioinfo.co.kr
๋ฐœ์†ก ์‹œ๊ฐ' + NOW + ' KST
SPF ์ธ์ฆPASS
DKIM ์„œ๋ช…์„œ๋ช…๋จ
\n' + + '

' + 'GUARDiA ITSM์€ SR ์ ‘์ˆ˜, ์ธ์‹œ๋˜ํŠธ ์•Œ๋ฆผ, SLA ๊ฒฝ๊ณ , ๋ฐฐํฌ ์•Œ๋ฆผ ๋“ฑ์„ ์ด๋ฉ”์ผ๋กœ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.

\n' + '
\n' + '

\n' + ' (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  | guardia@zioinfo.co.kr | http://101.79.17.164:8001\n' + '

\n' + '
\n' + '
\n' + '\n' + '\n' +) + +msg = MIMEMultipart('alternative') +msg['Subject'] = '[GUARDiA] ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ - ' + NOW +msg['From'] = '(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  GUARDiA <' + FROM + '>' +msg['To'] = TO +msg['Reply-To'] = FROM +msg['Date'] = formatdate(localtime=True) +msg['Message-ID'] = make_msgid(domain=DOMAIN) +msg['List-Unsubscribe'] = '' +msg['X-Mailer'] = 'GUARDiA ITSM v2.0' + +msg.attach(MIMEText(text, 'plain', 'utf-8')) +msg.attach(MIMEText(html, 'html', 'utf-8')) + +try: + with smtplib.SMTP('localhost', 25, timeout=15) as smtp: + smtp.ehlo('mail.zioinfo.co.kr') + smtp.sendmail(FROM, [TO], msg.as_string()) + print('OK ๋ฐœ์†ก ์™„๋ฃŒ') + print('์ˆ˜์‹ : ' + TO) + print('์‹œ๊ฐ: ' + NOW + ' KST') +except Exception as ex: + print('FAIL:', ex) diff --git a/deploy/guardia_mail_v3.py b/deploy/guardia_mail_v3.py new file mode 100644 index 0000000..fd418ce --- /dev/null +++ b/deploy/guardia_mail_v3.py @@ -0,0 +1,107 @@ +""" +GUARDiA ์ด๋ฉ”์ผ ๋ฐœ์†ก v3 โ€” ํ”ผ์‹ฑ ๊ฒฝ๊ณ  ์ œ๊ฑฐ +ํ•ต์‹ฌ ์ˆ˜์ •: IP ์ง์ ‘ ๋งํฌ ์ œ๊ฑฐ, ๋„๋ฉ”์ธ ์‚ฌ์šฉ, ๊ฐ„๊ฒฐํ•œ ๋ณธ๋ฌธ +""" +import smtplib +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.utils import formatdate, make_msgid +from datetime import datetime + +TO = 'ythong86@gmail.com' +FROM = 'guardia@zioinfo.co.kr' +DOMAIN = 'zioinfo.co.kr' +NOW = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + +# ํ…์ŠคํŠธ ๋ณธ๋ฌธ โ€” IP ๋งํฌ ์—†์Œ, ๊ฐ„๊ฒฐํ•˜๊ฒŒ +text = ( + '์•ˆ๋…•ํ•˜์„ธ์š”,\n\n' + '(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  GUARDiA ITSM ๋ฉ”์ผ ์„œ๋ฒ„ ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.\n\n' + '์ด ๋ฉ”์ผ์€ zio ์„œ๋ฒ„์˜ Postfix + OpenDKIM ์ •์ƒ ๋™์ž‘์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋ฐœ์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.\n\n' + '๋ฐœ์‹ ์ž: guardia@' + DOMAIN + '\n' + '๋ฐœ์†ก ์‹œ๊ฐ: ' + NOW + ' KST\n\n' + '-- \n' + '(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ \n' + 'guardia@' + DOMAIN + '\n' +) + +# HTML ๋ณธ๋ฌธ โ€” IP ๋งํฌ ์™„์ „ ์ œ๊ฑฐ, ์‹ฌํ”Œํ•˜๊ฒŒ +html = ( + '\n' + '\n' + 'GUARDiA ITSM\n' + '\n' + + '\n' + + ' \n' + ' \n' + ' \n' + + ' \n' + ' \n' + ' \n' + + ' \n' + ' \n' + ' \n' + + '
\n' + '

' + 'GUARDiA ITSM

\n' + '

' + '(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ 

\n' + '
\n' + '

' + '๋ฉ”์ผ ์„œ๋ฒ„ ํ…Œ์ŠคํŠธ

\n' + '

\n' + ' zio ์„œ๋ฒ„์˜ ๋ฉ”์ผ ์„œ๋น„์Šค๊ฐ€ ์ •์ƒ ๋™์ž‘ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
\n' + ' ๋ณธ ๋ฉ”์ผ์€ ์‹œ์Šคํ…œ ์ ๊ฒ€ ๋ชฉ์ ์œผ๋กœ ๋ฐœ์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.\n' + '

\n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + '
๋ฐœ์‹ ์ž' + 'guardia@' + DOMAIN + '
๋ฐœ์†ก ์‹œ๊ฐ' + NOW + ' KST
SPF / DKIM์ธ์ฆ ์™„๋ฃŒ
\n' + '
\n' + ' (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ   |  guardia@' + DOMAIN + '\n' + '
\n' + '\n' + '\n' +) + +msg = MIMEMultipart('alternative') +msg['Subject'] = '[GUARDiA] ๋ฉ”์ผ ์„œ๋ฒ„ ํ…Œ์ŠคํŠธ - ' + NOW +msg['From'] = '(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  GUARDiA <' + FROM + '>' +msg['To'] = TO +msg['Reply-To'] = FROM +msg['Date'] = formatdate(localtime=True) +msg['Message-ID'] = make_msgid(domain=DOMAIN) +msg['List-Unsubscribe'] = '' + +msg.attach(MIMEText(text, 'plain', 'utf-8')) +msg.attach(MIMEText(html, 'html', 'utf-8')) + +try: + with smtplib.SMTP('localhost', 25, timeout=15) as smtp: + smtp.ehlo('mail.' + DOMAIN) + smtp.sendmail(FROM, [TO], msg.as_string()) + print('OK ๋ฐœ์†ก ์™„๋ฃŒ - ' + TO) + print('์‹œ๊ฐ: ' + NOW + ' KST') + print('IP ๋งํฌ ์™„์ „ ์ œ๊ฑฐ / SPF+DKIM ์ ์šฉ') +except Exception as ex: + print('FAIL:', ex) diff --git a/deploy/install_ai.py b/deploy/install_ai.py new file mode 100644 index 0000000..47b0a1c --- /dev/null +++ b/deploy/install_ai.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +"""AI ํ”Œ๋žซํผ ์„ค์น˜ ์Šคํฌ๋ฆฝํŠธ""" +import paramiko, time, sys + +HOST='101.79.17.164'; USER='root'; PASS='1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +def run(label, cmd, timeout=300): + print(f'\n[{label}]') + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd) + start = time.time() + while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time()-start > timeout: print('[TIMEOUT]'); break + time.sleep(0.5) + while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + rc = chan.recv_exit_status() + print(f'exit={rc}'); return rc + +install_script = """#!/bin/bash +VENV=/opt/guardia/venv/bin/pip +$VENV install -q --upgrade langchain langchain-community langchain-ollama chromadb sentence-transformers +$VENV install -q langgraph langchain-chroma +echo DONE +""" +with sftp.open('/tmp/install_ai.sh', 'w') as f: + f.write(install_script) +sftp.close() + +run('AI ํŒจํ‚ค์ง€ ์„ค์น˜', 'bash /tmp/install_ai.sh 2>&1 | tail -10', 300) + +# ๊ฒ€์ฆ ์Šคํฌ๋ฆฝํŠธ +verify = """ +import sys +results = [] +for pkg in ['langchain', 'chromadb', 'langchain_community', 'langchain_ollama', 'langgraph']: + try: + m = __import__(pkg) + ver = getattr(m, '__version__', 'ok') + results.append('OK ' + pkg + '==' + str(ver)) + except ImportError as e: + results.append('FAIL ' + pkg + ': ' + str(e)) + +for r in results: + print(r) +ok_cnt = sum(1 for r in results if r.startswith('OK')) +print('\\n๊ฒฐ๊ณผ: ' + str(ok_cnt) + '/' + str(len(results)) + ' PASS') +""" +sftp = client.open_sftp() +with sftp.open('/tmp/verify_ai.py', 'w') as f: + f.write(verify) +sftp.close() + +run('์„ค์น˜ ๊ฒ€์ฆ', '/opt/guardia/venv/bin/python3 /tmp/verify_ai.py') + +# ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ ๋‹ค์šด๋กœ๋“œ ํ™•์ธ +run('nomic-embed-text ํ™•์ธ', 'ollama list | grep -i nomic || echo "not yet downloaded"') + +# ์ฝ”๋“œ๋ฒ ์ด์Šค ์ž„๋ฒ ๋”ฉ ์Šคํฌ๋ฆฝํŠธ ์ƒ์„ฑ +embed_script = '''#!/usr/bin/env python3 +"""GUARDiA ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ChromaDB์— ์ž„๋ฒ ๋”ฉ""" +import os, sys +os.environ["ANONYMIZED_TELEMETRY"] = "False" + +from langchain_community.document_loaders import DirectoryLoader, TextLoader +from langchain.text_splitter import RecursiveCharacterTextSplitter +from langchain_ollama import OllamaEmbeddings +from langchain_chroma import Chroma + +APP_DIR = "/opt/guardia/app" +VDB_DIR = "/opt/guardia/vectordb" + +print("๋ฌธ์„œ ๋กœ๋”ฉ...") +loader = DirectoryLoader(APP_DIR, glob="**/*.py", + loader_cls=TextLoader, loader_kwargs={"encoding":"utf-8", "autodetect_encoding":True}) +docs = loader.load() +print(f"๋กœ๋“œ๋œ ํŒŒ์ผ: {len(docs)}๊ฐœ") + +splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=100) +chunks = splitter.split_documents(docs) +print(f"์ฒญํฌ ์ˆ˜: {len(chunks)}") + +embeddings = OllamaEmbeddings(model="nomic-embed-text", base_url="http://localhost:11434") + +print("์ž„๋ฒ ๋”ฉ ์ค‘ (์‹œ๊ฐ„ ์†Œ์š”)...") +vectordb = Chroma.from_documents(chunks[:50], embeddings, # ์ฒ˜์Œ 50๊ฐœ๋งŒ ํ…Œ์ŠคํŠธ + persist_directory=VDB_DIR, collection_name="guardia_codebase") +print(f"์ž„๋ฒ ๋”ฉ ์™„๋ฃŒ. ์ €์žฅ ์œ„์น˜: {VDB_DIR}") + +# ํ…Œ์ŠคํŠธ ๊ฒ€์ƒ‰ +results = vectordb.similarity_search("๋ผ์ด์„ ์Šค ํ‚ค ๋“ฑ๋ก", k=2) +print("\\nํ…Œ์ŠคํŠธ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ:") +for r in results: + print(f" - {r.metadata.get('source','?')}: {r.page_content[:60]}...") +''' + +sftp = client.open_sftp() +with sftp.open('/opt/guardia/app/scripts/embed_codebase.py', 'w') as f: + f.write(embed_script) +run('embed_codebase.py ์ƒ์„ฑ', 'ls /opt/guardia/app/scripts/') +sftp.close() + +client.close() +print('\nAI ํ”Œ๋žซํผ ์„ค์น˜ ์™„๋ฃŒ') diff --git a/deploy/login_test.py b/deploy/login_test.py new file mode 100644 index 0000000..ed18318 --- /dev/null +++ b/deploy/login_test.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +"""GUARDiA ITSM ๋กœ๊ทธ์ธ ๋ฐฉ์‹ ํ™•์ธ ๋ฐ ๋ผ์ด์„ ์Šค ํ…Œ์ŠคํŠธ""" +import paramiko, time, sys, json + +HOST = '101.79.17.164'; USER = 'root'; PASS = '1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +test_py = '''#!/usr/bin/env python3 +import urllib.request, json + +BASE = "http://localhost:8001" + +# ๋กœ๊ทธ์ธ ๋ฐฉ์‹ ํƒ์ƒ‰ +login_attempts = [ + ("JSON username/password", "POST", "/api/auth/login", + json.dumps({"username":"admin","password":"1111"}).encode(), "application/json"), + ("JSON username/password (Admin2026)", "POST", "/api/auth/login", + json.dumps({"username":"admin","password":"Admin@2026!"}).encode(), "application/json"), + ("Form data", "POST", "/api/auth/login", + "username=admin&password=1111".encode(), "application/x-www-form-urlencoded"), +] + +TOKEN = None +for name, method, path, body, ct in login_attempts: + req = urllib.request.Request(f"{BASE}{path}", data=body, method=method) + req.add_header("Content-Type", ct) + try: + resp = urllib.request.urlopen(req, timeout=5) + d = json.loads(resp.read()) + if "access_token" in d: + TOKEN = d["access_token"] + print(f"OK ๋กœ๊ทธ์ธ ์„ฑ๊ณต [{name}]: token={TOKEN[:20]}...") + break + else: + print(f"FAIL [{name}]: {d}") + except urllib.error.HTTPError as e: + print(f"FAIL [{name}] HTTP {e.code}: {e.read().decode()[:100]}") + +if not TOKEN: + print("๋ชจ๋“  ๋กœ๊ทธ์ธ ์‹œ๋„ ์‹คํŒจ") + exit(1) + +# ๋ผ์ด์„ ์Šค API ํ…Œ์ŠคํŠธ +def api(method, path, data=None): + url = f"{BASE}{path}" + body = json.dumps(data).encode() if data else None + req = urllib.request.Request(url, data=body, method=method) + req.add_header("Content-Type", "application/json") + req.add_header("Authorization", f"Bearer {TOKEN}") + try: + resp = urllib.request.urlopen(req, timeout=10) + return json.loads(resp.read()), resp.status + except urllib.error.HTTPError as e: + return json.loads(e.read()), e.code + +print("\\n=== ๋ผ์ด์„ ์Šค API ํ…Œ์ŠคํŠธ ===") +RESULTS = [] + +def test(name, fn): + try: + r = fn(); ok = True + except Exception as ex: + r = str(ex); ok = False + RESULTS.append((name, ok, str(r)[:80])) + print(f\'{"OK" if ok else "FAIL"} {name}: {str(r)[:80]}\') + return r if ok else None + +# T2: ํ˜„์žฌ ์ƒํƒœ +test("T2 ๋ผ์ด์„ ์Šค ํ˜„์žฌ ์ƒํƒœ", lambda: api("GET", "/api/license/status")[0].get("message")) + +# T3: ์ฒดํ—˜ ๋ผ์ด์„ ์Šค (์—†์œผ๋ฉด ๋ฐœ๊ธ‰) +cur, _ = api("GET", "/api/license/status") +if not cur.get("activated"): + def t3(): + r,s = api("POST", "/api/license/trial", {"customer":"์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ์ฒดํ—˜ํŒ","days":7}) + return f"HTTP {s}: {r.get(\'message\', r.get(\'detail\', \'?\'))}" + test("T3 ์ฒดํ—˜ ๋ผ์ด์„ ์Šค ๋ฐœ๊ธ‰ (7์ผ)", t3) +else: + RESULTS.append(("T3 ์ฒดํ—˜ ๋ผ์ด์„ ์Šค ๋ฐœ๊ธ‰", True, "์ด๋ฏธ ํ™œ์„ฑํ™”๋จ")) + print("SKIP T3: ์ด๋ฏธ ํ™œ์„ฑํ™”๋จ") + +# T4: ํ™œ์„ฑํ™” ํ›„ ์ƒํƒœ +def t4(): + r,_ = api("GET", "/api/license/status") + return f"valid={r.get(\'valid\')}, edition={r.get(\'edition\')}, days={r.get(\'days_remaining\')}" +test("T4 ํ™œ์„ฑํ™” ํ›„ ์ƒํƒœ", t4) + +# T5: ์ด๋ ฅ +def t5(): + r,s = api("GET", "/api/license/history") + return f"HTTP {s}: {len(r) if isinstance(r,list) else \'?\'}๊ฑด" +test("T5 ๋ผ์ด์„ ์Šค ์ด๋ ฅ ์กฐํšŒ", t5) + +# T6: ์ž˜๋ชป๋œ ํ‚ค ๊ฒ€์ฆ +def t6(): + r,s = api("POST", "/api/license/verify", {"license_key":"invalid_key"}) + return f"HTTP {s}: {r.get(\'detail\', r.get(\'message\',\'?\'))[:50]}" +test("T6 ์ž˜๋ชป๋œ ํ‚ค ๊ฒ€์ฆ (400/422 ์˜ˆ์ƒ)", t6) + +# T7: Manager UI +try: + resp7 = urllib.request.urlopen("http://localhost:8090/", timeout=5) + test("T7 Manager UI", lambda: f"HTTP {resp7.status}") +except Exception as ex: + test("T7 Manager UI", lambda: f"ERROR: {str(ex)[:50]}") + +# T8: Manager API +try: + resp8 = urllib.request.urlopen("http://localhost:8002/health", timeout=5) + d8 = json.loads(resp8.read()) + test("T8 Manager Backend", lambda: d8.get("status","?")) +except Exception as ex: + test("T8 Manager Backend", lambda: f"ERROR: {str(ex)[:50]}") + +print(f"\\n{'='*55}") +passed = sum(1 for _,ok,_ in RESULTS if ok) +print(f"๊ฒฐ๊ณผ: {passed}/{len(RESULTS)} PASS") +for name,ok,detail in RESULTS: + print(f\' {"OK" if ok else "FAIL"} {name}\') +print("="*55) +''' +with sftp.open('/tmp/test_lic2.py', 'w') as f: + f.write(test_py) +sftp.close() + +chan = client.get_transport().open_session() +chan.set_combine_stderr(True) +chan.exec_command('python3 /tmp/test_lic2.py 2>&1') +start = time.time() +while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time() - start > 30: break + time.sleep(0.2) +while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) +sys.stdout.flush() +chan.recv_exit_status() +client.close() diff --git a/deploy/nginx_opennet.py b/deploy/nginx_opennet.py new file mode 100644 index 0000000..83a114b --- /dev/null +++ b/deploy/nginx_opennet.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +"""Nginx ๊ฐœ๋ฐฉ๋ง ์„ค์ • ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ""" +import paramiko, sys, time + +HOST = '101.79.17.164'; USER = 'root'; PASS = '1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +def run(label, cmd, timeout=30): + print(f'\n[{label}]') + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd) + start = time.time() + while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time() - start > timeout: print('[TIMEOUT]'); break + time.sleep(0.2) + while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + rc = chan.recv_exit_status() + print(f'exit={rc}'); return rc + +# GUARDiA ITSM HTTPS ์„ค์ • (ํฌํŠธ 443 + 8443) +guardia_https_nginx = r"""# โ”€โ”€ GUARDiA ITSM HTTPโ†’HTTPS ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +server { + listen 8001; + server_name _; + return 301 https://$host:8443$request_uri; +} + +# โ”€โ”€ GUARDiA ITSM HTTPS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +server { + listen 8443 ssl; + server_name _; + + ssl_certificate /etc/ssl/guardia/server.crt; + ssl_certificate_key /etc/ssl/guardia/server.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + + client_max_body_size 100M; + + # ๋ณด์•ˆ ํ—ค๋” + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options DENY always; + add_header X-Content-Type-Options nosniff always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + # Rate Limiting (๊ฐœ๋ฐฉ๋ง) + limit_req_zone $binary_remote_addr zone=guardia_api:10m rate=30r/m; + + location / { + proxy_pass http://127.0.0.1:8001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + proxy_read_timeout 300s; + } + + location /api/ { + limit_req zone=guardia_api burst=10 nodelay; + proxy_pass http://127.0.0.1:8001; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + proxy_read_timeout 60s; + } + + location /api/external/ { + limit_req zone=guardia_api burst=5 nodelay; + proxy_pass http://127.0.0.1:8001; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto https; + } + + location /ws/ { + proxy_pass http://127.0.0.1:8001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_read_timeout 3600s; + } +} +""" + +# ํ™ˆํŽ˜์ด์ง€ HTTPS ์„ค์ • (ํฌํŠธ 443) +zioinfo_https_nginx = r"""# โ”€โ”€ ์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ํ™ˆํŽ˜์ด์ง€ HTTPโ†’HTTPS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +server { + listen 80 default_server; + server_name _; + return 301 https://$host$request_uri; +} + +# โ”€โ”€ ์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ํ™ˆํŽ˜์ด์ง€ HTTPS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +server { + listen 443 ssl; + server_name _; + + ssl_certificate /etc/ssl/guardia/server.crt; + ssl_certificate_key /etc/ssl/guardia/server.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; + ssl_prefer_server_ciphers off; + + root /var/www/zioinfo; + index index.html; + + add_header Strict-Transport-Security "max-age=31536000" always; + add_header X-Frame-Options SAMEORIGIN always; + add_header X-Content-Type-Options nosniff always; + + location / { + try_files $uri $uri/ /index.html; + add_header Cache-Control no-cache; + } + location /api/ { + proxy_pass http://127.0.0.1:8082; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto https; + proxy_read_timeout 60s; + } + location ~* \.(js|css|png|jpg|gif|ico|svg|woff2)$ { + expires 30d; + add_header Cache-Control "public, immutable"; + } + gzip on; + gzip_types text/plain text/css application/javascript application/json; +} +""" + +with sftp.open('/etc/nginx/sites-available/guardia-https', 'w') as f: + f.write(guardia_https_nginx) +with sftp.open('/etc/nginx/sites-available/zioinfo-https', 'w') as f: + f.write(zioinfo_https_nginx) +sftp.close() + +# ์‹ฌ๋ณผ๋ฆญ ๋งํฌ ๊ต์ฒด +run('Nginx ์‚ฌ์ดํŠธ ํ™œ์„ฑํ™”', + 'ln -sf /etc/nginx/sites-available/guardia-https /etc/nginx/sites-enabled/guardia && ' + 'ln -sf /etc/nginx/sites-available/zioinfo-https /etc/nginx/sites-enabled/zioinfo && ' + 'rm -f /etc/nginx/sites-enabled/guardia-https /etc/nginx/sites-enabled/zioinfo-https && ' + 'echo links_ok') + +run('UFW 443/8443 ํฌํŠธ ์˜คํ”ˆ', + 'ufw allow 443/tcp && ufw allow 8443/tcp && echo ufw_ok') + +run('Nginx ์„ค์ • ๊ฒ€์ฆ', + 'nginx -t') + +run('Nginx ๋ฆฌ๋กœ๋“œ', + 'systemctl reload nginx && echo nginx_reloaded') + +client.close() +print('\nHTTPS ์„ค์ • ์™„๋ฃŒ') diff --git a/deploy/notif_test_server.py b/deploy/notif_test_server.py new file mode 100644 index 0000000..8d8a9a8 --- /dev/null +++ b/deploy/notif_test_server.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +"""์„œ๋ฒ„์—์„œ ์ง์ ‘ ์‹คํ–‰๋˜๋Š” ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ""" +import paramiko, time, sys + +HOST='101.79.17.164'; USER='root'; PASS='1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +script = r""" +import http.client, json, sys, time + +HOST = 'localhost' +PORT = 8001 +RESULTS = [] + +def api(method, path, data=None, token=None): + conn = http.client.HTTPConnection(HOST, PORT, timeout=10) + headers = {'Content-Type':'application/json'} + if token: headers['Authorization'] = 'Bearer ' + token + body = json.dumps(data).encode() if data else None + conn.request(method, path, body=body, headers=headers) + r = conn.getresponse() + raw = r.read() + try: return json.loads(raw), r.status + except: return {'raw': raw.decode()[:200]}, r.status + +def test(name, fn): + try: + r = fn(); ok = True + except Exception as e: + r = str(e)[:80]; ok = False + RESULTS.append((name, ok, str(r)[:80])) + print(('PASS' if ok else 'FAIL') + ' ' + name + ': ' + str(r)[:80]) + return r if ok else None + +print() +print('='*60) +print('GUARDiA ITSM -> Messenger ์•Œ๋ฆผ ์†ก์ˆ˜์‹  ํ…Œ์ŠคํŠธ') +print('='*60) + +# T1: ๋กœ๊ทธ์ธ +d, s = api('POST', '/api/auth/login', {'username':'admin','password':'1111'}) +TOKEN = d.get('access_token','') +test('T1 ๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ', lambda: 'OK token=' + TOKEN[:15] + '...' if TOKEN else 'FAIL: ' + str(d)) + +# T2: WebSocket ์—ฐ๊ฒฐ ์ˆ˜ ํ™•์ธ +test('T2 WebSocket ์ƒํƒœ', lambda: str(api('GET','/api/ws/status',token=TOKEN))) + +# T3: SR ๋“ฑ๋ก โ†’ ์ด๋ฒคํŠธ ๋ฐœ์ƒ +import hashlib; sr_suffix = hashlib.md5(str(time.time()).encode()).hexdigest()[:6].upper() +def t3(): + d, s = api('POST','/api/tasks', { + 'title': f'[์•Œ๋ฆผํ…Œ์ŠคํŠธ] Messenger ์—ฐ๋™ SR-{sr_suffix}', + 'description': 'GUARDiA Messenger WebSocket ์•Œ๋ฆผ ์—ฐ๋™ ํ…Œ์ŠคํŠธ. ์•ฑ ์•Œ๋ฆผํƒญ์—์„œ ํ™•์ธํ•˜์„ธ์š”.', + 'priority':'MEDIUM', 'sr_type':'OTHER'}, token=TOKEN) + return f'HTTP {s} sr_id={d.get("sr_id","?")} title={d.get("title","?")[:30]}' +test('T3 SR ๋“ฑ๋ก (WebSocket ์ด๋ฒคํŠธ ํŠธ๋ฆฌ๊ฑฐ)', t3) + +# T4: ์•Œ๋ฆผ ๋กœ๊ทธ ์กฐํšŒ +def t4(): + d, s = api('GET','/api/notifications/log?size=5',token=TOKEN) + cnt = len(d) if isinstance(d,list) else '?' + return f'HTTP {s} count={cnt}' +test('T4 ์•Œ๋ฆผ ๋กœ๊ทธ ์กฐํšŒ', t4) + +# T5: ๋ฉ”์‹ ์ € ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ +def t5(): + d, s = api('POST','/api/notifications/test-messenger',token=TOKEN) + return f'HTTP {s}: {d.get("message",d.get("detail",str(d)[:50]))}' +test('T5 ๋ฉ”์‹ ์ € ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ API', t5) + +# T6: ์ด๋ฉ”์ผ ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ (ythong86@gmail.com) +def t6(): + d, s = api('POST','/api/notifications/test-email', + {'email':'ythong86@gmail.com','subject':'GUARDiA Messenger ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ', + 'message':'GUARDiA Messenger ์•ฑ์ด ์ •์ƒ์ ์œผ๋กœ ITSM๊ณผ ์—ฐ๋™๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'}, + token=TOKEN) + return f'HTTP {s}: {d.get("message",str(d)[:50])}' +test('T6 ์ด๋ฉ”์ผ ์•Œ๋ฆผ ๋ฐœ์†ก (ythong86@gmail.com)', t6) + +# T7: ๋Œ€์‹œ๋ณด๋“œ API (์•ฑ ๋ฉ”์ธํ™”๋ฉด ์—ฐ๋™) +def t7(): + d, s = api('GET','/api/dashboard',token=TOKEN) + keys = list(d.keys())[:4] + return f'HTTP {s} keys={keys}' +test('T7 ๋Œ€์‹œ๋ณด๋“œ API (์•ฑ ์—ฐ๋™)', t7) + +# T8: SR ๋ชฉ๋ก ์กฐํšŒ (์•ฑ SRํƒญ ์—ฐ๋™) +def t8(): + d, s = api('GET','/api/tasks?size=3',token=TOKEN) + cnt = d.get('total_elements', d.get('total', '?')) + return f'HTTP {s} total={cnt}' +test('T8 SR ๋ชฉ๋ก API (์•ฑ SRํƒญ ์—ฐ๋™)', t8) + +print() +print('='*60) +passed = sum(1 for _,ok,_ in RESULTS if ok) +print(f'๊ฒฐ๊ณผ: {passed}/{len(RESULTS)} PASS') +for name,ok,d in RESULTS: + print(('OK ' if ok else 'FAIL') + ' ' + name) +print('='*60) +print() +print('[Messenger ์•ฑ ์•Œ๋ฆผ ํ™•์ธ ๋ฐฉ๋ฒ•]') +print('1. ์•ฑ ์‹คํ–‰ -> ์•Œ๋ฆผ ํƒญ') +print('2. ์ƒ๋‹จ: "GUARDiA ์‹ค์‹œ๊ฐ„ ์—ฐ๊ฒฐ" ์ดˆ๋ก ํ‘œ์‹œ ํ™•์ธ') +print('3. SR ๋“ฑ๋ก ์ฆ‰์‹œ -> ์•Œ๋ฆผ ๋ชฉ๋ก์— [์‹ค์‹œ๊ฐ„] ๋ฐฐ์ง€๋กœ ํ‘œ์‹œ') +print('4. ๋ฐฐํฌ ์‹คํ–‰ ์‹œ -> ๋ฐฐํฌ ์™„๋ฃŒ/์‹คํŒจ ์•Œ๋ฆผ ์ž๋™ ์ˆ˜์‹ ') +""" + +with sftp.open('/tmp/notif_test.py','w') as f: f.write(script) +sftp.close() + +chan = client.get_transport().open_session() +chan.set_combine_stderr(True) +chan.exec_command('python3 /tmp/notif_test.py 2>&1') +start = time.time() +while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time()-start > 30: break + time.sleep(0.3) +while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) +sys.stdout.flush() +chan.recv_exit_status() +client.close() diff --git a/deploy/notif_test_v2.py b/deploy/notif_test_v2.py new file mode 100644 index 0000000..ff493ea --- /dev/null +++ b/deploy/notif_test_v2.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +"""GUARDiA ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ v2 - ์ •ํ™•ํ•œ API ๊ฒฝ๋กœ ์‚ฌ์šฉ""" +import paramiko, time, sys + +HOST='101.79.17.164'; USER='root'; PASS='1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +script = r""" +import http.client, json, sys, time, secrets + +HOST = 'localhost'; PORT = 8001 +RESULTS = [] + +def api(method, path, data=None, token=None): + conn = http.client.HTTPConnection(HOST, PORT, timeout=10) + headers = {'Content-Type':'application/json'} + if token: headers['Authorization'] = 'Bearer ' + token + body = json.dumps(data).encode() if data else None + conn.request(method, path, body=body, headers=headers) + r = conn.getresponse() + raw = r.read() + try: return json.loads(raw), r.status + except: return {'raw': raw.decode()[:200]}, r.status + +def test(name, fn): + try: + r = fn(); ok = True + except Exception as e: r = str(e)[:80]; ok = False + RESULTS.append((name, ok, str(r)[:100])) + print(('PASS' if ok else 'FAIL') + ' ' + name + ': ' + str(r)[:90]) + return r if ok else None + +print() +print('='*65) +print('GUARDiA ITSM -> Messenger ์•Œ๋ฆผ ์†ก์ˆ˜์‹  ํ…Œ์ŠคํŠธ v2') +print('='*65) + +# T1: ๋กœ๊ทธ์ธ +d, s = api('POST', '/api/auth/login', {'username':'admin','password':'1111'}) +TOKEN = d.get('access_token','') +test('T1 ๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ', lambda: f'HTTP {s} OK' if TOKEN else f'FAIL: {d}') + +# T2: WebSocket ์—ฐ๊ฒฐ ์ˆ˜ (์•ฑ์ด ์—ฐ๊ฒฐ๋˜๋ฉด 1 ์ด์ƒ) +def t2(): + d, s = api('GET','/api/ws/status',token=TOKEN) + conns = d.get('total_connections', d.get('connection_count', 0)) + msg = f'์—ฐ๊ฒฐ ์ˆ˜={conns}' + if conns == 0: msg += ' (์•ฑ์ด ์„œ๋ฒ„์— ์—ฐ๊ฒฐ๋˜๋ฉด ์ˆซ์ž ์ฆ๊ฐ€)' + return f'HTTP {s} {msg}' +test('T2 WebSocket ์—ฐ๊ฒฐ ์ƒํƒœ', t2) + +# T3: ์˜ฌ๋ฐ”๋ฅธ SR ๋“ฑ๋ก API ํƒ์ƒ‰ +def t3(): + # tasks API ํ™•์ธ + d, s = api('GET','/api/tasks?size=1',token=TOKEN) + if isinstance(d, list): + return f'HTTP {s} listํ˜•ํƒœ SR {len(d)}๊ฑด' + elif isinstance(d, dict): + cnt = d.get('total_elements', d.get('total', d.get('count','?'))) + return f'HTTP {s} total={cnt}' + return f'HTTP {s} {type(d).__name__}' +test('T3 SR ๋ชฉ๋ก ์กฐํšŒ (์•ฑ SRํƒญ)', t3) + +# T4: SR ์‹ ๊ทœ ๋“ฑ๋ก (์˜ฌ๋ฐ”๋ฅธ ํ•„๋“œ) +sr_id_suffix = secrets.token_hex(3).upper() +def t4(): + # GUARDiA ITSM์˜ nlcmd (์ž์—ฐ์–ด ๋ช…๋ น) ๋˜๋Š” tasks๋กœ SR ๋“ฑ๋ก + # ๊ธฐ๊ด€ ์ฝ”๋“œ ์—†์ด ๋“ฑ๋ก ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธ + d, s = api('POST','/api/tasks', { + 'title': f'[์•Œ๋ฆผํ…Œ์ŠคํŠธ] Messenger ์—ฐ๋™ {sr_id_suffix}', + 'description': 'GUARDiA Messenger WebSocket ์•Œ๋ฆผ ์—ฐ๋™ ํ…Œ์ŠคํŠธ', + 'priority':'MEDIUM', + 'sr_type':'INQUIRY', + 'inst_id': None, + }, token=TOKEN) + if s in [200,201]: + return f'HTTP {s} SR๋“ฑ๋ก: {d.get("sr_id","?")} -> WebSocket ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ๋ฐœ์ƒ' + return f'HTTP {s}: {d.get("detail","?")[:60]}' +test('T4 SR ๋“ฑ๋ก (WebSocket ์ด๋ฒคํŠธ ํŠธ๋ฆฌ๊ฑฐ)', t4) + +# T5: ์‹ค์‹œ๊ฐ„ ์ด๋ฒคํŠธ ์ง์ ‘ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ (๋‚ด๋ถ€ API) +def t5(): + # SSE/WebSocket ์ง์ ‘ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ํ…Œ์ŠคํŠธ + d, s = api('POST','/api/notifications/test-messenger', token=TOKEN) + return f'HTTP {s}: {str(d)[:80]}' +test('T5 ๋ฉ”์‹ ์ € ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ (๋‚ด๋ถ€)', t5) + +# T6: ์•Œ๋ฆผ ๋กœ๊ทธ ํ™•์ธ (์ด๋ฒคํŠธ ๊ธฐ๋ก) +def t6(): + d, s = api('GET','/api/notifications/log?size=10', token=TOKEN) + if isinstance(d, list): + return f'HTTP {s} {len(d)}๊ฑด ๋กœ๊ทธ' + return f'HTTP {s} {d.get("total","?")}๊ฑด' +test('T6 ์•Œ๋ฆผ ๋กœ๊ทธ ํ™•์ธ', t6) + +# T7: ๊ฐ์‚ฌ ๋กœ๊ทธ์—์„œ ์ด๋ฒคํŠธ ํ™•์ธ +def t7(): + d, s = api('GET','/api/audit?size=3', token=TOKEN) + if isinstance(d, list) and len(d) > 0: + latest = d[0] + return f'HTTP {s} ์ตœ์‹ : {latest.get("action","?")} by {latest.get("username","?")}' + elif isinstance(d, dict): + items = d.get('items', d.get('content', [])) + if items: + return f'HTTP {s} ์ตœ์‹ : {items[0].get("action","?")} by {items[0].get("username","?")}' + return f'HTTP {s} {type(d).__name__}' +test('T7 ๊ฐ์‚ฌ ๋กœ๊ทธ (์ด๋ฒคํŠธ ์ถ”์ )', t7) + +# T8: AI ์ฑ—๋ด‡ API (์•ฑ ์ฑ—ํƒญ ์—ฐ๋™) +def t8(): + d, s = api('POST','/api/chatbot/message', + {'message':'์•ˆ๋…•ํ•˜์„ธ์š”, Messenger ์—ฐ๋™ ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.'}, + token=TOKEN) + reply = d.get('reply', d.get('message', d.get('response', str(d)[:50]))) + return f'HTTP {s} ์‘๋‹ต: {str(reply)[:60]}' +test('T8 AI ์ฑ—๋ด‡ API (์•ฑ ์ฑ—ํƒญ ์—ฐ๋™)', t8) + +print() +print('='*65) +passed = sum(1 for _,ok,_ in RESULTS if ok) +print(f'๊ฒฐ๊ณผ: {passed}/{len(RESULTS)} PASS') +print() +for name,ok,detail in RESULTS: + print(f' {"OK " if ok else "FAIL"} {name}') +print() +print('='*65) +print() +print('[์•ฑ ์•Œ๋ฆผ ๋™์ž‘ ํ๋ฆ„]') +print() +print(' GUARDiA ITSM GUARDiA Messenger ์•ฑ') +print(' | |') +print(' SR ๋“ฑ๋ก (POST /api/tasks) WebSocket ์—ฐ๊ฒฐ๋จ') +print(' | |') +print(' events.broadcast() ํ˜ธ์ถœ ์ด๋ฒคํŠธ ์ˆ˜์‹  (lastEvent)') +print(' | |') +print(' ws.broadcast() ํ˜ธ์ถœ ์•Œ๋ฆผ ํƒญ์— ์ฆ‰์‹œ ํ‘œ์‹œ') +print(' | |') +print(' tb_notification_log ์ €์žฅ โšก ์‹ค์‹œ๊ฐ„ ๋ฐฐ์ง€ ํ‘œ์‹œ') +print() +print('[Messenger ์•ฑ์—์„œ ํ™•์ธํ•  ๋‚ด์šฉ]') +print('1. ์•Œ๋ฆผ ํƒญ ์ƒ๋‹จ: "GUARDiA ์‹ค์‹œ๊ฐ„ ์—ฐ๊ฒฐ" + ์ดˆ๋ก ๊นœ๋นก์ž„') +print('2. SR ๋“ฑ๋ก ์ฆ‰์‹œ: โšก ์‹ค์‹œ๊ฐ„ ๋ฐฐ์ง€์™€ ํ•จ๊ป˜ ์•Œ๋ฆผ ํ•ญ๋ชฉ ์ถ”๊ฐ€') +print('3. ๋ฐฐํฌ ์‹คํ–‰ ์‹œ: ๐Ÿš€ ๋ฐฐํฌ ์™„๋ฃŒ ์•Œ๋ฆผ ์ž๋™ ํ‘œ์‹œ') +print('4. ์ธ์‹œ๋˜ํŠธ ๋ฐœ์ƒ: ๐Ÿšจ ๊ธด๊ธ‰ ์•Œ๋ฆผ ์ฆ‰์‹œ ์ˆ˜์‹ ') +""" + +with sftp.open('/tmp/notif_v2.py','w') as f: f.write(script) +sftp.close() + +chan = client.get_transport().open_session() +chan.set_combine_stderr(True) +chan.exec_command('python3 /tmp/notif_v2.py 2>&1') +start = time.time() +while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(8192)); sys.stdout.flush() + if time.time()-start > 40: break + time.sleep(0.3) +while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(8192)) +sys.stdout.flush() +chan.recv_exit_status() +client.close() diff --git a/deploy/send_guardia_mail.py b/deploy/send_guardia_mail.py new file mode 100644 index 0000000..53e2d6b --- /dev/null +++ b/deploy/send_guardia_mail.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +"""GUARDiA ์ด๋ฉ”์ผ ๋ฐœ์†ก ์Šคํฌ๋ฆฝํŠธ โ€” ์„œ๋ฒ„์—์„œ ์ง์ ‘ ์‹คํ–‰""" +import paramiko, time, sys + +HOST = '101.79.17.164'; USER = 'root'; PASS = '1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +# ์„œ๋ฒ„์—์„œ ์‹คํ–‰ํ•  Python ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํŒŒ์ผ๋กœ ์ €์žฅ +mail_script = r""" +import smtplib +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from datetime import datetime + +TO = 'ythong86@gmail.com' +FROM = 'guardia@zioinfo.co.kr' +NOW = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + +text_body = """์•ˆ๋…•ํ•˜์„ธ์š”, + +GUARDiA ITSM ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. + +๋ฐœ์‹  ์„œ๋ฒ„: mail.zioinfo.co.kr (101.79.17.164) +๋ฐœ์‹ ์ž: guardia@zioinfo.co.kr +๋ฐœ์†ก ์‹œ๊ฐ: """ + NOW + """ KST +์ธ์ฆ: SPF PASS + DKIM ์„œ๋ช… + +์ด ๋ฉ”์ผ์ด ์ˆ˜์‹ ๋˜๋ฉด GUARDiA SMTP ์„œ๋ฒ„๊ฐ€ ์ •์ƒ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  | GUARDiA ITSM v2.0 +http://101.79.17.164:8001 +""" + +html_body = """ + + + +
+
+ 🛡 +
+

GUARDiA ITSM

+

(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  AI ์ธํ”„๋ผ ์ž์œจ ์šด์˜ ํ”Œ๋žซํผ

+
+
+
+

📧 ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ

+

zio ์„œ๋ฒ„ SMTP ์„œ๋ฒ„ ์ •์ƒ ๋™์ž‘ ํ™•์ธ

+
+

✅ ๋ฐœ์†ก ์„ฑ๊ณต

+

SPF ยท DKIM ์ธ์ฆ์„ ํ†ต๊ณผํ•˜์—ฌ ์ •์ƒ ๋ฐœ์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

+
+ + + + + + + + + + + + + + + + + + + + + +
๋ฐœ์‹  ์„œ๋ฒ„mail.zioinfo.co.kr (101.79.17.164)
๋ฐœ์‹ ์žguardia@zioinfo.co.kr
๋ฐœ์†ก ์‹œ๊ฐ""" + NOW + """ KST
SPF ์ธ์ฆ + ✅ PASS +
DKIM ์„œ๋ช… + ✅ ์„œ๋ช…๋จ +
+
+

🤖 GUARDiA ITSM ์ด๋ฉ”์ผ ์•Œ๋ฆผ ๊ธฐ๋Šฅ

+
    +
  • SR ์ ‘์ˆ˜/์™„๋ฃŒ ์•Œ๋ฆผ
  • +
  • ์ธ์‹œ๋˜ํŠธ ๊ธด๊ธ‰ ์•Œ๋ฆผ
  • +
  • SLA ์œ„๋ฐ˜ ๊ฒฝ๊ณ 
  • +
  • ๋ฐฐํฌ ์™„๋ฃŒ/์‹คํŒจ ์•Œ๋ฆผ
  • +
  • ๋ผ์ด์„ ์Šค ๋งŒ๋ฃŒ ์•Œ๋ฆผ
  • +
+
+
+
+ GUARDiA ITSM v2.0 ์ž๋™ ๋ฐœ์†ก | (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ 
+ guardia@zioinfo.co.kr | http://101.79.17.164:8001 +
+
+ +""" + +msg = MIMEMultipart('alternative') +msg['Subject'] = '[GUARDiA] SMTP ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ - ' + NOW +msg['From'] = 'GUARDiA ITSM <' + FROM + '>' +msg['To'] = TO +msg['X-Mailer'] = 'GUARDiA ITSM v2.0' +msg.attach(MIMEText(text_body, 'plain', 'utf-8')) +msg.attach(MIMEText(html_body, 'html', 'utf-8')) + +try: + with smtplib.SMTP('localhost', 25, timeout=15) as smtp: + smtp.ehlo('mail.zioinfo.co.kr') + refused = smtp.sendmail(FROM, [TO], msg.as_string()) + if refused: + print('FAIL ๊ฑฐ๋ถ€:', refused) + else: + print('OK ๋ฐœ์†ก ์™„๋ฃŒ! ythong86@gmail.com ์ˆ˜์‹ ํ•จ์„ ํ™•์ธํ•˜์„ธ์š”.') + print('๋ฐœ์‹ : ' + FROM) + print('๋ฐœ์†ก ์‹œ๊ฐ: ' + NOW) +except Exception as ex: + print('FAIL:', ex) +""" + +with sftp.open('/tmp/guardia_mail.py', 'w') as f: + f.write(mail_script) +sftp.close() + +chan = client.get_transport().open_session() +chan.set_combine_stderr(True) +chan.exec_command('python3 /tmp/guardia_mail.py 2>&1') +start = time.time() +while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time() - start > 20: break + time.sleep(0.3) +while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) +sys.stdout.flush() +chan.recv_exit_status() +client.close() diff --git a/deploy/send_test_email.py b/deploy/send_test_email.py new file mode 100644 index 0000000..7100bbd --- /dev/null +++ b/deploy/send_test_email.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +""" +zio ์„œ๋ฒ„ SMTP(localhost:25/587)๋ฅผ ํ†ตํ•ด ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ +์„œ๋ฒ„์—์„œ ์ง์ ‘ ์‹คํ–‰ +""" +import smtplib, ssl, socket +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from datetime import datetime + +TO = 'ythong86@gmail.com' +FROM = 'guardia@zioinfo.co.kr' +HOST = 'localhost' + +def make_message(to, frm, subject, body_text, body_html=None): + msg = MIMEMultipart('alternative') + msg['Subject'] = subject + msg['From'] = f'GUARDiA ITSM <{frm}>' + msg['To'] = to + msg['X-Mailer']= 'GUARDiA ITSM v2.0' + msg.attach(MIMEText(body_text, 'plain', 'utf-8')) + if body_html: + msg.attach(MIMEText(body_html, 'html', 'utf-8')) + return msg + +html_body = f""" + + + + +
+ +
+
+ ๐Ÿ›ก๏ธ +
+

GUARDiA ITSM

+

(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  AI ์ธํ”„๋ผ ์ž์œจ ์šด์˜ ํ”Œ๋žซํผ

+
+
+
+ +
+

๐Ÿ“ง ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ

+
+

โœ… ๋ฐœ์†ก ์„ฑ๊ณต

+

+ zio ์„œ๋ฒ„์˜ Postfix SMTP ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด ์ด๋ฉ”์ผ์ด ์ •์ƒ ๋ฐœ์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค. +

+
+ + + + + + + + + + + + + + + + + + + + + +
๋ฐœ์‹  ์„œ๋ฒ„mail.zioinfo.co.kr (101.79.17.164)
๋ฐœ์‹ ์žguardia@zioinfo.co.kr
๋ฐœ์†ก ์‹œ๊ฐ{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} KST
SMTP ํฌํŠธ25 (Postfix)
์„œ๋ฒ„ OSUbuntu 24.04 LTS (NCloud)
+ +
+

๐Ÿค– GUARDiA ITSM ๊ธฐ๋Šฅ ์•ˆ๋‚ด

+
    +
  • SR(์„œ๋น„์Šค ์š”์ฒญ) ์ ‘์ˆ˜ ์•Œ๋ฆผ
  • +
  • ์ธ์‹œ๋˜ํŠธ ๋ฐœ์ƒ ๋ฐ ๊ธด๊ธ‰ ์•Œ๋ฆผ
  • +
  • SLA ์œ„๋ฐ˜ ๊ฒฝ๊ณ 
  • +
  • ๋ฐฐํฌ ์™„๋ฃŒ/์‹คํŒจ ์•Œ๋ฆผ
  • +
  • ๋ผ์ด์„ ์Šค ๋งŒ๋ฃŒ ์•Œ๋ฆผ
  • +
+
+
+ +
+ ์ด ๋ฉ”์ผ์€ GUARDiA ITSM ์ž๋™ ๋ฐœ์†ก ์‹œ์Šคํ…œ์—์„œ ๋ฐœ์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
+ (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  | guardia@zioinfo.co.kr | http://101.79.17.164:8001 +
+
+ + +""" + +text_body = f"""GUARDiA ITSM ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ + +โœ… ๋ฐœ์†ก ์„ฑ๊ณต +zio ์„œ๋ฒ„์˜ Postfix SMTP ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด ์ด๋ฉ”์ผ์ด ์ •์ƒ ๋ฐœ์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +๋ฐœ์‹  ์„œ๋ฒ„: mail.zioinfo.co.kr (101.79.17.164) +๋ฐœ์‹ ์ž: guardia@zioinfo.co.kr +๋ฐœ์†ก ์‹œ๊ฐ: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} KST +SMTP ํฌํŠธ: 25 (Postfix) + +GUARDiA ITSM ๊ธฐ๋Šฅ: +- SR(์„œ๋น„์Šค ์š”์ฒญ) ์ ‘์ˆ˜ ์•Œ๋ฆผ +- ์ธ์‹œ๋˜ํŠธ ๋ฐœ์ƒ ๋ฐ ๊ธด๊ธ‰ ์•Œ๋ฆผ +- SLA ์œ„๋ฐ˜ ๊ฒฝ๊ณ  +- ๋ฐฐํฌ ์™„๋ฃŒ/์‹คํŒจ ์•Œ๋ฆผ + +(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  | guardia@zioinfo.co.kr +""" + +print(f'์ˆ˜์‹ ์ž: {TO}') +print(f'๋ฐœ์‹ ์ž: {FROM}') +print(f'SMTP: {HOST}:25') +print() + +RESULTS = [] +# ๋ฐฉ๋ฒ• 1: localhost:25 ์ง์ ‘ ์—ฐ๊ฒฐ +print('--- ๋ฐฉ๋ฒ• 1: Postfix localhost:25 ---') +try: + msg = make_message(TO, FROM, f'[GUARDiA] ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ - {datetime.now().strftime("%H:%M")}', + text_body, html_body) + with smtplib.SMTP(HOST, 25, timeout=15) as smtp: + smtp.ehlo('mail.zioinfo.co.kr') + refused = smtp.sendmail(FROM, [TO], msg.as_string()) + if refused: + print(f'FAIL ๊ฑฐ๋ถ€๋œ ์ˆ˜์‹ ์ž: {refused}') + RESULTS.append(('localhost:25', False, str(refused))) + else: + print('OK ๋ฐœ์†ก ์™„๋ฃŒ (ํ์— ์ถ”๊ฐ€๋จ)') + RESULTS.append(('localhost:25', True, 'ํ ๋“ฑ๋ก ์„ฑ๊ณต')) +except Exception as ex: + print(f'FAIL {ex}') + RESULTS.append(('localhost:25', False, str(ex)[:80])) + +print() +print('--- ๊ฒฐ๊ณผ ---') +for method, ok, detail in RESULTS: + icon = 'OK ' if ok else 'FAIL' + print(f'{icon} {method}: {detail}') diff --git a/deploy/setup_cicd.py b/deploy/setup_cicd.py new file mode 100644 index 0000000..eec01fe --- /dev/null +++ b/deploy/setup_cicd.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +"""Gitea ์›นํ›… + Jenkins CI/CD ์™„์ „ ์ž๋™ํ™” ์„ค์ •""" +import paramiko, time, sys, json + +HOST = '101.79.17.164'; USER = 'root'; PASS = '1q2w3e!Q' +GITEA_USER = 'zio'; GITEA_PASS = 'Zio@Admin2026!' + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +def run(label, cmd, timeout=30): + print(f'\n[{label}]') + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd) + start = time.time() + while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time() - start > timeout: break + time.sleep(0.2) + while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + rc = chan.recv_exit_status() + print(f'exit={rc}'); return rc + +# 1. Jenkins ์ดˆ๊ธฐ ์„ค์ • ์Šคํ‚ต & ๋ณด์•ˆ ์™„ํ™” (๋กœ์ปฌ ์ „์šฉ) +run('Jenkins ์ดˆ๊ธฐ์„ค์ • ์Šคํ‚ต', + 'echo 2 > /var/lib/jenkins/jenkins.install.UpgradeWizard.state && ' + 'chown jenkins:jenkins /var/lib/jenkins/jenkins.install.UpgradeWizard.state') + +# Jenkins๊ฐ€ CSRF ์—†์ด ํŠธ๋ฆฌ๊ฑฐ ํ—ˆ์šฉํ•˜๋„๋ก ์„ค์ • (๋‚ด๋ถ€๋ง ์ „์šฉ) +jenkins_config_groovy = """ +import jenkins.model.* +import hudson.security.* + +def instance = Jenkins.getInstance() + +// CSRF ์„ค์ • ์™„ํ™” (๋‚ด๋ถ€๋ง ์ „์šฉ) +instance.setCrumbIssuer(null) +instance.save() +println "CSRF disabled for internal CI/CD" +""" +with sftp.open('/var/lib/jenkins/init.groovy.d/disable_csrf.groovy', 'w') as f: + f.write(jenkins_config_groovy) +run('๊ถŒํ•œ ์„ค์ •', 'chown -R jenkins:jenkins /var/lib/jenkins/init.groovy.d && systemctl restart jenkins && sleep 8 && systemctl is-active jenkins') + +# 2. Jenkins ๋นŒ๋“œ ํŠธ๋ฆฌ๊ฑฐ (CSRF ๋น„ํ™œ์„ฑํ™” ํ›„) +chan = client.get_transport().open_session() +chan.exec_command('cat /var/lib/jenkins/secrets/initialAdminPassword') +jenkins_pw = chan.makefile().read().decode().strip() +chan.recv_exit_status() + +trigger_script = f"""#!/usr/bin/env python3 +import urllib.request, base64, time + +PW = '{jenkins_pw}' +cred = base64.b64encode(f'admin:{{PW}}'.encode()).decode() +headers = {{'Authorization': f'Basic {{cred}}'}} + +for attempt in range(5): + try: + req = urllib.request.Request( + 'http://localhost:8080/job/zioinfo-web/build', + data=b'', headers=headers) + resp = urllib.request.urlopen(req, timeout=10) + print(f'๋นŒ๋“œ ํŠธ๋ฆฌ๊ฑฐ ์„ฑ๊ณต HTTP {{resp.status}}') + break + except Exception as e: + print(f'์‹œ๋„ {{attempt+1}}: {{e}}') + time.sleep(3) +""" +with sftp.open('/tmp/trigger.py', 'w') as f: + f.write(trigger_script) + +run('Jenkins ๋นŒ๋“œ ํŠธ๋ฆฌ๊ฑฐ', 'python3 /tmp/trigger.py') + +# 3. Gitea ์›นํ›… ํ™•์ธ / ์žฌ์„ค์ • (Jenkins ๋นŒ๋“œ URL๋กœ) +run('Gitea ์›นํ›… ๋ชฉ๋ก ํ™•์ธ', + 'curl -s http://localhost:3000/api/v1/repos/zio/zioinfo-web/hooks ' + '-u "zio:Zio@Admin2026!" | python3 -c "import json,sys; ' + 'hooks=json.load(sys.stdin); ' + '[print(h[chr(105)+chr(100)], h[chr(99)+chr(111)+chr(110)+chr(102)+chr(105)+chr(103)][chr(117)+chr(114)+chr(108)]) for h in hooks]"') + +# ์›นํ›…์„ Jenkins ๋นŒ๋“œ ์ง์ ‘ ํŠธ๋ฆฌ๊ฑฐ URL๋กœ ๊ต์ฒด +run('Gitea ์›นํ›… ์—…๋ฐ์ดํŠธ', + f'curl -s -X DELETE http://localhost:3000/api/v1/repos/zio/zioinfo-web/hooks/1 ' + f'-u "{GITEA_USER}:{GITEA_PASS}" && ' + f'curl -s -X DELETE http://localhost:3000/api/v1/repos/zio/zioinfo-web/hooks/2 ' + f'-u "{GITEA_USER}:{GITEA_PASS}" && ' + f'curl -s -X POST http://localhost:3000/api/v1/repos/zio/zioinfo-web/hooks ' + f'-H "Content-Type: application/json" ' + f'-u "{GITEA_USER}:{GITEA_PASS}" ' + f'-d \'{{"type":"gitea","active":true,' + f'"config":{{"url":"http://admin:{jenkins_pw}@localhost:8080/job/zioinfo-web/build",' + f'"content_type":"json"}},' + f'"events":["push"]}}\' | ' + f'python3 -c "import json,sys; d=json.load(sys.stdin); print(chr(87)+chr(101)+chr(98)+chr(104)+chr(111)+chr(111)+chr(107)+chr(32)+chr(73)+chr(68)+chr(58), d.get(chr(105)+chr(100)))"') + +sftp.close() +client.close() +print('\nCI/CD ์„ค์ • ์™„๋ฃŒ!') +print(f'Jenkins: http://{HOST}:8080 (์ดˆ๊ธฐPW: {jenkins_pw})') +print(f'Gitea: http://{HOST}:3000 (zio / Zio@Admin2026!)') +print(f'ํ™ˆํŽ˜์ด์ง€: http://{HOST}') +print(f'๊ด€๋ฆฌ์ž: http://{HOST}/admin') diff --git a/deploy/setup_guardia_cicd.py b/deploy/setup_guardia_cicd.py new file mode 100644 index 0000000..3477a94 --- /dev/null +++ b/deploy/setup_guardia_cicd.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +"""GUARDiA ITSM Gitea Push + CI/CD Webhook ์„ค์ •""" +import paramiko, time, sys, os, io, zipfile + +HOST='101.79.17.164'; USER='root'; PASS='1q2w3e!Q' +LOCAL_ITSM='C:/GUARDiA/itsm' +SEP=chr(92) + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +def run(label, cmd, timeout=60): + print(f'\n[{label}]') + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd) + start = time.time() + while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time()-start > timeout: print('[TIMEOUT]'); break + time.sleep(0.3) + while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + rc = chan.recv_exit_status() + print(f'exit={rc}'); return rc + +# ITSM ์†Œ์Šค ํŒจํ‚ค์ง• +SKIP_DIRS = {'__pycache__', '.git', 'backups', '_workspace', '.claude', 'node_modules', 'uploads'} +SKIP_EXTS = ('.pyc', '.db', '.db.bak', '.log') + +zip_buf = io.BytesIO(); count = 0 +with zipfile.ZipFile(zip_buf, 'w', zipfile.ZIP_DEFLATED) as zf: + for root, dirs, files in os.walk(LOCAL_ITSM): + dirs[:] = [d for d in dirs if d not in SKIP_DIRS] + rel = os.path.relpath(root, LOCAL_ITSM).replace(SEP, '/') + for f in files: + if f.endswith(SKIP_EXTS) or f.startswith('test_'): continue + arc = f if rel == '.' else f'{rel}/{f}' + zf.write(os.path.join(root, f), arc); count += 1 +zip_buf.seek(0) +with sftp.open('/tmp/guardia-src.zip', 'wb') as f: f.write(zip_buf.read()) +print(f'{count}๊ฐœ ํŒŒ์ผ ์—…๋กœ๋“œ') + +# push ์Šคํฌ๋ฆฝํŠธ ์ƒ์„ฑ +push_script = """#!/bin/bash +rm -rf /tmp/grd-push +mkdir /tmp/grd-push +cd /tmp/grd-push +unzip -q /tmp/guardia-src.zip +git init -q +git config user.email ci@zioinfo.co.kr +git config user.name ZioCI +git add -A +git commit -q -m "feat: GUARDiA ITSM v2.0 initial commit" +git remote add origin http://zio:Zio%40Admin2026%21@localhost:3000/zio/guardia-itsm.git +git push -f origin HEAD:main 2>&1 +echo "PUSH_DONE" +""" +with sftp.open('/tmp/push_guardia.sh', 'w') as f: f.write(push_script) + +# Webhook ์„ค์ • ์Šคํฌ๋ฆฝํŠธ +webhook_script = """#!/usr/bin/env python3 +import urllib.request, json, base64 + +GITEA = 'http://localhost:3000' +cred = base64.b64encode(b'zio:Zio@Admin2026!').decode() +headers = {'Content-Type':'application/json','Authorization':f'Basic {cred}'} + +# ๊ธฐ์กด ์›นํ›… ์‚ญ์ œ +for hook_id in [1,2,3,4]: + req = urllib.request.Request(f'{GITEA}/api/v1/repos/zio/guardia-itsm/hooks/{hook_id}', + method='DELETE', headers=headers) + try: urllib.request.urlopen(req) + except: pass + +# ์ƒˆ ์›นํ›… ๋“ฑ๋ก +data = json.dumps({ + 'type': 'gitea', 'active': True, + 'config': {'url': 'http://localhost:9999/', 'content_type': 'json', 'secret': 'zioinfo-deploy-2026'}, + 'events': ['push'] +}).encode() +req = urllib.request.Request(f'{GITEA}/api/v1/repos/zio/guardia-itsm/hooks', + data=data, method='POST', headers=headers) +resp = urllib.request.urlopen(req) +d = json.loads(resp.read()) +print('Webhook ID:', d.get('id')) +""" +with sftp.open('/tmp/setup_webhook.py', 'w') as f: f.write(webhook_script) + +sftp.close() + +run('Gitea push', 'bash /tmp/push_guardia.sh', 60) +run('Webhook ์„ค์ •', 'python3 /tmp/setup_webhook.py') + +# Deploy ์„œ๋ฒ„์— guardia ๋ฐฐํฌ ํ•ธ๋“ค๋Ÿฌ ์ถ”๊ฐ€ +deploy_handler_script = """ +import os, sys +os.chdir('/opt/guardia/app') + +# guardia deploy server ์—…๋ฐ์ดํŠธ +deploy_server_path = '/opt/zioinfo/deploy_server.py' +with open(deploy_server_path, 'r') as f: + content = f.read() + +# guardia ๋ฐฐํฌ ์Šคํ… ํ™•์ธ +if 'guardia' not in content: + print('Deploy server์— guardia ์Šคํ… ์ถ”๊ฐ€ ํ•„์š”') +else: + print('Deploy server: guardia ์ด๋ฏธ ํฌํ•จ๋จ') +""" +sftp2 = client.open_sftp() +with sftp2.open('/tmp/check_deploy.py', 'w') as f: f.write(deploy_handler_script) +sftp2.close() +run('Deploy ์„œ๋ฒ„ ํ™•์ธ', 'python3 /tmp/check_deploy.py') + +# ๋ธŒ๋žœ์น˜ ํ™•์ธ +run('์ €์žฅ์†Œ ๋ธŒ๋žœ์น˜ ํ™•์ธ', + 'curl -s http://localhost:3000/api/v1/repos/zio/guardia-itsm ' + '-u zio:Zio@Admin2026! | python3 -c "import json,sys; d=json.load(sys.stdin); ' + 'print(chr(78)+chr(97)+chr(109)+chr(101)+chr(58), d.get(chr(102)+chr(117)+chr(108)+chr(108)+chr(95)+chr(110)+chr(97)+chr(109)+chr(101)), ' + 'chr(66)+chr(114)+chr(97)+chr(110)+chr(99)+chr(104)+chr(58), d.get(chr(100)+chr(101)+chr(102)+chr(97)+chr(117)+chr(108)+chr(116)+chr(95)+chr(98)+chr(114)+chr(97)+chr(110)+chr(99)+chr(104)))"') + +client.close() +print('\nGUARDiA CI/CD ๊ตฌ์ถ• ์™„๋ฃŒ') diff --git a/deploy/setup_messenger_gitea.py b/deploy/setup_messenger_gitea.py new file mode 100644 index 0000000..a3483eb --- /dev/null +++ b/deploy/setup_messenger_gitea.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""GUARDiA Messenger Gitea ์ €์žฅ์†Œ ์ƒ์„ฑ + ์ฝ”๋“œ Push + Webhook ์„ค์ •""" +import paramiko, time, sys, os, io, zipfile, urllib.request, json, base64 + +HOST = '101.79.17.164'; USER = 'root'; PASS = '1q2w3e!Q' +LOCAL_APP = 'C:/GUARDiA/app' +SEP = chr(92) +GITEA = 'http://localhost:3000' +GIT_USER = 'zio' +GIT_PASS = 'Zio@Admin2026!' +cred = base64.b64encode(f'{GIT_USER}:{GIT_PASS}'.encode()).decode() +headers = {'Content-Type': 'application/json', 'Authorization': f'Basic {cred}'} + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +def run(label, cmd, timeout=60): + print(f'\n[{label}]') + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd) + start = time.time() + while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time()-start > timeout: print('[TIMEOUT]'); break + time.sleep(0.3) + while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + rc = chan.recv_exit_status() + print(f'exit={rc}'); return rc + +def gitea_api(method, path, data=None): + url = f'{GITEA}/api/v1{path}' + body = json.dumps(data).encode() if data else None + req = urllib.request.Request(url, data=body, method=method, headers=headers) + try: + resp = urllib.request.urlopen(req, timeout=10) + return json.loads(resp.read()), resp.status + except urllib.error.HTTPError as e: + body = e.read() + try: return json.loads(body), e.code + except: return {'error': body.decode()[:200]}, e.code + +# 1. ์ €์žฅ์†Œ ์ƒ์„ฑ +print('=== 1. Gitea ์ €์žฅ์†Œ ์ƒ์„ฑ ===') +d, s = gitea_api('POST', '/user/repos', { + 'name': 'guardia-messenger', + 'description': 'GUARDiA Messenger โ€” React Native + Expo SDK 51 ๋ชจ๋ฐ”์ผ ์•ฑ', + 'private': False, + 'auto_init': False, +}) +print(f'HTTP {s}: {d.get("full_name", d.get("message", d))}') + +# 2. ์†Œ์Šค zip ์ƒ์„ฑ (node_modules, .git, android, ios ์ œ์™ธ) +print('\n=== 2. ์†Œ์Šค ํŒจํ‚ค์ง• ===') +SKIP_DIRS = {'node_modules', '.git', 'android', 'ios', '__pycache__', '.expo'} +SKIP_EXTS = ('.pyc', '.log') + +zip_buf = io.BytesIO(); count = 0 +with zipfile.ZipFile(zip_buf, 'w', zipfile.ZIP_DEFLATED) as zf: + for root, dirs, files in os.walk(LOCAL_APP): + dirs[:] = [d for d in dirs if d not in SKIP_DIRS] + rel = os.path.relpath(root, LOCAL_APP).replace(SEP, '/') + for f in files: + if f.endswith(SKIP_EXTS): continue + arc = f if rel == '.' else f'{rel}/{f}' + zf.write(os.path.join(root, f), arc); count += 1 +zip_buf.seek(0) +with sftp.open('/tmp/messenger-src.zip', 'wb') as f: f.write(zip_buf.read()) +print(f' {count}๊ฐœ ํŒŒ์ผ ์—…๋กœ๋“œ') + +# 3. ์„œ๋ฒ„์—์„œ git push +print('\n=== 3. Gitea Push ===') +push_script = f"""#!/bin/bash +rm -rf /tmp/messenger-push +mkdir /tmp/messenger-push +cd /tmp/messenger-push +unzip -q /tmp/messenger-src.zip +git init -q +git config user.email ci@zioinfo.co.kr +git config user.name ZioCI +git add -A +git commit -q -m "feat: GUARDiA Messenger v1.0.0 ์ดˆ๊ธฐ ์ปค๋ฐ‹ + +- React Native + Expo SDK 51 + TypeScript +- 6๊ฐœ ํ™”๋ฉด: ๋กœ๊ทธ์ธยท๋Œ€์‹œ๋ณด๋“œยทSRยทAI์ฑ—๋ด‡ยท์•Œ๋ฆผยท์„ค์ • +- EAS Build (APK ์„ฑ๊ณต: 51096ada) +- .easignore: android/ios ์ œ์™ธ +- plugins/withGradleProps: PNG Crunching ๋น„ํ™œ์„ฑํ™”" +git remote add origin http://zio:Zio%40Admin2026%21@localhost:3000/zio/guardia-messenger.git +git push -f origin HEAD:main 2>&1 +echo "PUSH_DONE" +""" +with sftp.open('/tmp/push_messenger.sh', 'w') as f: f.write(push_script) +sftp.close() +run('Git Push', 'bash /tmp/push_messenger.sh', 60) + +# 4. Webhook ๋“ฑ๋ก (Deploy Webhook) +print('\n=== 4. Webhook ๋“ฑ๋ก ===') +wh, ws = gitea_api('POST', '/repos/zio/guardia-messenger/hooks', { + 'type': 'gitea', + 'active': True, + 'config': { + 'url': 'http://localhost:9999/', + 'content_type': 'json', + 'secret': 'zioinfo-deploy-2026', + }, + 'events': ['push'], +}) +print(f'Webhook HTTP {ws}: ID={wh.get("id", wh.get("message", "?"))}') + +# 5. ๋ธŒ๋žœ์น˜ ํ™•์ธ +print('\n=== 5. ์ €์žฅ์†Œ ํ™•์ธ ===') +repo, _ = gitea_api('GET', '/repos/zio/guardia-messenger') +print(f'์ €์žฅ์†Œ: {repo.get("full_name")}') +print(f'๋ธŒ๋žœ์น˜: {repo.get("default_branch")}') +print(f'URL: {repo.get("html_url")}') + +client.close() +print('\n=== ์™„๋ฃŒ ===') +print('Gitea: http://101.79.17.164:3000/zio/guardia-messenger') diff --git a/deploy/setup_smtp.py b/deploy/setup_smtp.py new file mode 100644 index 0000000..6860fdb --- /dev/null +++ b/deploy/setup_smtp.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +""" +Postfix + Dovecot SMTP/IMAP ์„œ๋ฒ„ ์„ค์น˜ +๋„๋ฉ”์ธ: zioinfo.co.kr (IP ๊ธฐ๋ฐ˜, DNS MX ์—†์–ด๋„ ๋กœ์ปฌ ๋ฐœ์†ก ๊ฐ€๋Šฅ) +๊ณ„์ •: info, admin, ythong, choyounbun, guardia / ๋น„๋ฐ€๋ฒˆํ˜ธ: 1q2w3e!Q +""" +import paramiko, time, sys + +HOST='101.79.17.164'; USER='root'; PASS='1q2w3e!Q' +DOMAIN = 'zioinfo.co.kr' +ACCOUNTS = ['info', 'admin', 'ythong', 'choyounbun', 'guardia'] +MAIL_PASS = '1q2w3e!Q' + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +def run(label, cmd, timeout=120): + print(f'\n[{label}]') + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(f'export DEBIAN_FRONTEND=noninteractive; {cmd}') + start = time.time() + while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time()-start > timeout: print('[TIMEOUT]'); break + time.sleep(0.3) + while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + rc = chan.recv_exit_status() + print(f'exit={rc}'); return rc + +# 1. Postfix + Dovecot ์„ค์น˜ +run('Postfix + Dovecot ์„ค์น˜', + 'apt-get install -y -q postfix dovecot-imapd dovecot-pop3d mailutils', + 180) + +# 2. Postfix ์„ค์ • +postfix_main_cf = f"""# Postfix Main Configuration โ€” {DOMAIN} +smtpd_banner = $myhostname ESMTP +biff = no +append_dot_mydomain = no + +# ํ˜ธ์ŠคํŠธ๋ช… ์„ค์ • +myhostname = mail.{DOMAIN} +myorigin = /etc/mailname +mydomain = {DOMAIN} +mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain + +# ๋„คํŠธ์›Œํฌ +inet_interfaces = all +inet_protocols = ipv4 +mynetworks = 127.0.0.0/8 + +# ๋ฉ”์ผ๋ฐ•์Šค (Maildir ํ˜•์‹) +home_mailbox = Maildir/ +mailbox_size_limit = 0 + +# SMTP ์ธ์ฆ (Dovecot SASL) +smtpd_sasl_type = dovecot +smtpd_sasl_path = private/auth +smtpd_sasl_auth_enable = yes +smtpd_sasl_security_options = noanonymous + +# TLS (์ž์ฒด์„œ๋ช… ์ธ์ฆ์„œ ์‚ฌ์šฉ) +smtpd_tls_cert_file = /etc/ssl/guardia/server.crt +smtpd_tls_key_file = /etc/ssl/guardia/server.key +smtpd_use_tls = yes +smtpd_tls_session_cache_database = btree:${{data_directory}}/smtpd_scache +smtp_tls_session_cache_database = btree:${{data_directory}}/smtp_scache + +# ๋ณด์•ˆ +smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination +recipient_delimiter = + +""" + +dovecot_conf = f"""# Dovecot Configuration โ€” {DOMAIN} +protocols = imap pop3 lmtp +listen = * + +# ์ธ์ฆ ๋ฉ”์ปค๋‹ˆ์ฆ˜ +auth_mechanisms = plain login + +# ๋ฉ”์ผ ์ €์žฅ์†Œ (Maildir) +mail_location = maildir:~/Maildir + +# Postfix SASL ์†Œ์ผ“ +service auth {{ + unix_listener /var/spool/postfix/private/auth {{ + mode = 0660 + user = postfix + group = postfix + }} +}} + +# SSL/TLS +ssl = yes +ssl_cert = /dev/null || useradd -m -s /bin/false {acc} && ' + f'echo \"{acc}:{MAIL_PASS}\" | chpasswd && ' + f'mkdir -p /home/{acc}/Maildir/{{cur,new,tmp}} && ' + f'chown -R {acc}:{acc} /home/{acc}/Maildir && ' + f'echo \"{acc}@{DOMAIN} created\"', + 15) + +# 4. UFW ํฌํŠธ ์˜คํ”ˆ +run('UFW SMTP/IMAP ํฌํŠธ', + 'ufw allow 25/tcp && ufw allow 587/tcp && ' + 'ufw allow 993/tcp && ufw allow 995/tcp && ' + 'ufw allow 143/tcp && ufw allow 110/tcp && echo ok') + +# 5. Postfix master.cf์— submission ํฌํŠธ ์ถ”๊ฐ€ +run('Postfix submission ํฌํŠธ ํ™œ์„ฑํ™”', + 'grep -q "^submission" /etc/postfix/master.cf || ' + 'echo "submission inet n - y - - smtpd" >> /etc/postfix/master.cf') + +# 6. ์„œ๋น„์Šค ์‹œ์ž‘ +run('Postfix ์‹œ์ž‘', 'systemctl enable postfix && systemctl restart postfix && systemctl is-active postfix') +run('Dovecot ์‹œ์ž‘', 'systemctl enable dovecot && systemctl restart dovecot && systemctl is-active dovecot') + +# 7. ๋กœ์ปฌ ๋ฉ”์ผ ํ…Œ์ŠคํŠธ +run('๋กœ์ปฌ ๋ฉ”์ผ ๋ฐœ์†ก ํ…Œ์ŠคํŠธ', + f'echo "GUARDiA SMTP ํ…Œ์ŠคํŠธ ๋ฉ”์ผ" | mail -s "Test" info@{DOMAIN} 2>/dev/null && echo "๋ฉ”์ผ ๋ฐœ์†ก OK" || echo "mail command ํ™•์ธ ํ•„์š”"', + 10) + +sftp.close(); client.close() +print(f'\nSMTP ์„œ๋ฒ„ ๊ตฌ์ถ• ์™„๋ฃŒ!') +print(f'๋„๋ฉ”์ธ: @{DOMAIN}') +print(f'๊ณ„์ •: {", ".join(ACCOUNTS)}') +print(f'SMTP: mail.{DOMAIN}:25 (๋กœ์ปฌ) / 587 (STARTTLS)') +print(f'IMAP: mail.{DOMAIN}:993 (SSL)') diff --git a/deploy/test_export_import.py b/deploy/test_export_import.py new file mode 100644 index 0000000..c99e610 --- /dev/null +++ b/deploy/test_export_import.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +"""Export/Import API ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ""" +import paramiko, time, sys + +HOST='101.79.17.164'; USER='root'; PASS='1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +test_code = r""" +import urllib.request, json, io, zipfile + +BASE = 'http://localhost:8001' +body = json.dumps({'username':'admin','password':'1111'}).encode() +req = urllib.request.Request(BASE+'/api/auth/login', data=body, method='POST') +req.add_header('Content-Type','application/json') +TOKEN = json.loads(urllib.request.urlopen(req,timeout=10).read()).get('access_token','') +print('Token:', TOKEN[:20]+'...') + +def api(method, path, token=TOKEN): + req = urllib.request.Request(BASE+path, method=method) + req.add_header('Authorization','Bearer '+TOKEN) + try: + r = urllib.request.urlopen(req, timeout=15) + return json.loads(r.read()), r.status + except urllib.error.HTTPError as e: + try: return json.loads(e.read()), e.code + except: return {}, e.code + +RESULTS = [] +def test(name, fn): + try: + r = fn(); ok = True + except Exception as ex: + r = str(ex); ok = False + RESULTS.append((name, ok, str(r)[:80])) + print(('OK ' if ok else 'FAIL') + ' ' + name + ': ' + str(r)[:80]) + return r + +print() +print('=== Export/Import API ํ…Œ์ŠคํŠธ ===') + +test('T1 SR Export', lambda: api('GET','/api/export-import/export/sr?limit=5')) +test('T2 CMDB Export', lambda: api('GET','/api/export-import/export/cmdb')) +test('T3 ๊ธฐ๊ด€ Export', lambda: api('GET','/api/export-import/export/institutions')) +test('T4 ๊ฐ์‚ฌ ๋กœ๊ทธ Export', lambda: api('GET','/api/export-import/export/audit?limit=5')) + +def t5_bundle(): + req = urllib.request.Request(BASE+'/api/export-import/export/bundle', method='GET') + req.add_header('Authorization','Bearer '+TOKEN) + r = urllib.request.urlopen(req, timeout=20) + data = r.read() + with zipfile.ZipFile(io.BytesIO(data)) as z: + names = z.namelist() + return 'HTTP '+str(r.status)+' ZIP='+str(len(data)//1024)+'KB files='+str(names) +test('T5 ๋ฒˆ๋“ค ZIP Export', t5_bundle) + +def t6_dry_run(): + sr_data = json.dumps({'data':[{'sr_id':'DRYTEST-001','title':'Dry Run Test SR', + 'status':'RECEIVED','priority':'LOW'}]}).encode() + boundary = b'testboundary123' + form = (b'--'+boundary+b'\r\n' + +b'Content-Disposition: form-data; name="file"; filename="test.json"\r\n' + +b'Content-Type: application/json\r\n\r\n' + +sr_data+b'\r\n--'+boundary+b'--\r\n') + req = urllib.request.Request(BASE+'/api/export-import/import/sr?dry_run=true', + data=form, method='POST') + req.add_header('Content-Type','multipart/form-data; boundary=testboundary123') + req.add_header('Authorization','Bearer '+TOKEN) + r = urllib.request.urlopen(req, timeout=10) + d = json.loads(r.read()) + return 'HTTP '+str(r.status)+' status='+str(d.get('status'))+' total='+str(d.get('total')) +test('T6 SR Import dry_run', t6_dry_run) + +r7 = urllib.request.urlopen('http://localhost:8090/export-import', timeout=5) +test('T7 Manager UI /export-import', lambda: 'HTTP '+str(r7.status)) + +print() +print('='*55) +passed = sum(1 for _,ok,_ in RESULTS if ok) +print('๊ฒฐ๊ณผ: '+str(passed)+'/'+str(len(RESULTS))+' PASS') +for name,ok,detail in RESULTS: + print((' OK ' if ok else ' FAIL') + ' ' + name) +print('='*55) +""" + +with sftp.open('/tmp/test_ei.py', 'w') as f: + f.write(test_code) +sftp.close() + +chan = client.get_transport().open_session() +chan.set_combine_stderr(True) +chan.exec_command('python3 /tmp/test_ei.py 2>&1') +start = time.time() +while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(8192)); sys.stdout.flush() + if time.time() - start > 40: break + time.sleep(0.2) +while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(8192)) +sys.stdout.flush() +chan.recv_exit_status() +client.close() diff --git a/deploy/test_license.py b/deploy/test_license.py new file mode 100644 index 0000000..fba97a4 --- /dev/null +++ b/deploy/test_license.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +"""๋ผ์ด์„ ์Šค API ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ""" +import paramiko, time, sys, json, urllib.request, urllib.parse + +HOST = '101.79.17.164'; USER = 'root'; PASS = '1q2w3e!Q' +BASE = f'http://{HOST}:8001' + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) + +def ssh(cmd, timeout=15): + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd) + start = time.time() + while not chan.exit_status_ready(): + if time.time() - start > timeout: break + time.sleep(0.2) + out = b'' + while chan.recv_ready(): out += chan.recv(8192) + chan.recv_exit_status() + return out.decode('utf-8', errors='replace').strip() + +def api(method, path, data=None, token=None): + url = f'{BASE}{path}' + body = json.dumps(data).encode() if data else None + req = urllib.request.Request(url, data=body, method=method) + req.add_header('Content-Type', 'application/json') + if token: + req.add_header('Authorization', f'Bearer {token}') + try: + resp = urllib.request.urlopen(req, timeout=10) + return json.loads(resp.read()), resp.status + except urllib.error.HTTPError as e: + return json.loads(e.read()), e.code + +RESULTS = [] + +def test(name, fn): + try: + result = fn() + RESULTS.append((name, True, result)) + print(f'โœ… {name}: {str(result)[:100]}') + return result + except Exception as e: + RESULTS.append((name, False, str(e))) + print(f'โŒ {name}: {e}') + return None + +# T1: ๋กœ๊ทธ์ธ +params = urllib.parse.urlencode({'username': 'admin', 'password': 'Admin@zioinfo2026!'}) +req = urllib.request.Request(f'{BASE}/api/auth/login', + data=params.encode(), method='POST') +req.add_header('Content-Type', 'application/x-www-form-urlencoded') +resp = urllib.request.urlopen(req, timeout=10) +token_data = json.loads(resp.read()) +TOKEN = token_data.get('access_token', '') +print(f'\n=== ๋ผ์ด์„ ์Šค API ํ…Œ์ŠคํŠธ (์„œ๋ฒ„: {HOST}) ===') +print(f'TOKEN: {TOKEN[:25]}...\n') + +# T2: ํ˜„์žฌ ์ƒํƒœ +test('T2 ๋ผ์ด์„ ์Šค ํ˜„์žฌ ์ƒํƒœ', lambda: api('GET', '/api/license/status', token=TOKEN)[0].get('message')) + +# T3: ์ฒดํ—˜ ์ด๋ ฅ ํ™•์ธ +prev_status, _ = api('GET', '/api/license/status', token=TOKEN) +already_activated = prev_status.get('activated', False) + +if not already_activated: + # T3: ์ฒดํ—˜ ๋ผ์ด์„ ์Šค ๋ฐœ๊ธ‰ + def trial_test(): + r, s = api('POST', '/api/license/trial', + {'customer': '์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ์ฒดํ—˜ํŒ', 'days': 7}, token=TOKEN) + return f"HTTP {s}: {r.get('message', r.get('detail', '?'))}" + test('T3 ์ฒดํ—˜ ๋ผ์ด์„ ์Šค ๋ฐœ๊ธ‰', trial_test) +else: + print('T3 ์ฒดํ—˜ ๋ผ์ด์„ ์Šค ๋ฐœ๊ธ‰: ์ด๋ฏธ ํ™œ์„ฑํ™”๋จ โ€” SKIP') + RESULTS.append(('T3 ์ฒดํ—˜ ๋ผ์ด์„ ์Šค ๋ฐœ๊ธ‰', True, '์ด๋ฏธ ํ™œ์„ฑํ™”๋จ (๊ธฐ์กด ์œ ์ง€)')) + +# T4: ํ™œ์„ฑํ™” ํ›„ ์ƒํƒœ ํ™•์ธ +def status_after(): + r, _ = api('GET', '/api/license/status', token=TOKEN) + return f"valid={r.get('valid')}, edition={r.get('edition')}, days={r.get('days_remaining')}" +test('T4 ํ™œ์„ฑํ™” ํ›„ ์ƒํƒœ', status_after) + +# T5: ๋ผ์ด์„ ์Šค ์ด๋ ฅ +def history_test(): + r, s = api('GET', '/api/license/history', token=TOKEN) + cnt = len(r) if isinstance(r, list) else '?' + return f"HTTP {s}: {cnt}๊ฑด ์ด๋ ฅ" +test('T5 ๋ผ์ด์„ ์Šค ์ด๋ ฅ ์กฐํšŒ', history_test) + +# T6: ํ‚ค ๊ฒ€์ฆ (์œ ํšจํ•˜์ง€ ์•Š์€ ํ‚ค) +def verify_invalid(): + r, s = api('POST', '/api/license/verify', {'license_key': 'invalid_key_test'}, token=TOKEN) + return f"HTTP {s}: {r.get('detail', r.get('message', '?'))[:60]}" +test('T6 ์ž˜๋ชป๋œ ํ‚ค ๊ฒ€์ฆ (400 ์˜ˆ์ƒ)', verify_invalid) + +# T7: Manager UI ์ ‘์† +ui_resp = ssh('curl -s -o /dev/null -w "%{http_code}" http://localhost:8090/licenses 2>/dev/null || ' + 'curl -s -o /dev/null -w "%{http_code}" http://localhost:8090/') +test('T7 Manager UI (๋ผ์ด์„ ์Šค ํŽ˜์ด์ง€)', lambda: f'HTTP {ui_resp}') + +# T8: GUARDiA Manager API ์ƒํƒœ +api_resp = ssh('curl -s http://localhost:8002/health 2>/dev/null') +test('T8 Manager Backend ์ƒํƒœ', lambda: api_resp[:80]) + +# ๊ฒฐ๊ณผ ์š”์•ฝ +print(f'\n{"="*50}') +passed = sum(1 for _, ok, _ in RESULTS if ok) +print(f'ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ: {passed}/{len(RESULTS)} PASS') +for name, ok, detail in RESULTS: + print(f' {"โœ…" if ok else "โŒ"} {name}') +print('='*50) + +client.close() diff --git a/deploy/test_license_remote.py b/deploy/test_license_remote.py new file mode 100644 index 0000000..c8cd8c0 --- /dev/null +++ b/deploy/test_license_remote.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +"""์„œ๋ฒ„์—์„œ ์ง์ ‘ ๋ผ์ด์„ ์Šค API ํ…Œ์ŠคํŠธ (paramiko exec)""" +import paramiko, time, sys, json + +HOST = '101.79.17.164'; USER = 'root'; PASS = '1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) + +def ssh(label, cmd, timeout=20): + print(f'\n[{label}]') + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd) + start = time.time() + while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time() - start > timeout: break + time.sleep(0.2) + while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + rc = chan.recv_exit_status() + print(f' exit={rc}') + return rc + +# ํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ ์„œ๋ฒ„์— ์—…๋กœ๋“œ +test_script = r"""#!/usr/bin/env python3 +import urllib.request, urllib.parse, json + +BASE = 'http://localhost:8001' +RESULTS = [] + +def api(method, path, data=None, token=None): + url = f'{BASE}{path}' + body = json.dumps(data).encode() if data else None + req = urllib.request.Request(url, data=body, method=method) + req.add_header('Content-Type', 'application/json') + if token: req.add_header('Authorization', f'Bearer {token}') + try: + resp = urllib.request.urlopen(req, timeout=10) + return json.loads(resp.read()), resp.status + except urllib.error.HTTPError as e: + return json.loads(e.read()), e.code + except Exception as ex: + return {'error': str(ex)}, 0 + +def test(name, fn): + try: + r = fn(); ok = True + except Exception as e: + r = str(e); ok = False + RESULTS.append((name, ok, r)) + print(f'{"OK" if ok else "FAIL"} {name}: {str(r)[:80]}') + return r if ok else None + +print('=== GUARDiA ๋ผ์ด์„ ์Šค API ํ…Œ์ŠคํŠธ ===') + +# T1 ๋กœ๊ทธ์ธ +params = urllib.parse.urlencode({'username':'admin','password':'Admin@zioinfo2026!'}) +req = urllib.request.Request(f'{BASE}/api/auth/login', data=params.encode(), method='POST') +req.add_header('Content-Type', 'application/x-www-form-urlencoded') +resp = urllib.request.urlopen(req, timeout=10) +TOKEN = json.loads(resp.read()).get('access_token','') +print(f'TOKEN: {TOKEN[:20]}...\n') + +# T2 ํ˜„์žฌ ์ƒํƒœ +test('T2 ํ˜„์žฌ ๋ผ์ด์„ ์Šค ์ƒํƒœ', lambda: api('GET', '/api/license/status', token=TOKEN)[0].get('message')) + +# T3 ์ฒดํ—˜ ๋ฐœ๊ธ‰ (์ด๋ฏธ ์žˆ์œผ๋ฉด Skip) +cur, _ = api('GET', '/api/license/status', token=TOKEN) +if not cur.get('activated'): + def t3(): + r,s = api('POST','/api/license/trial',{'customer':'์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ์ฒดํ—˜ํŒ','days':7},token=TOKEN) + return f'HTTP {s}: {r.get("message",r.get("detail","?"))}' + test('T3 ์ฒดํ—˜ ๋ผ์ด์„ ์Šค ๋ฐœ๊ธ‰ (7์ผ)', t3) +else: + RESULTS.append(('T3 ์ฒดํ—˜ ๋ผ์ด์„ ์Šค ๋ฐœ๊ธ‰','SKIP','์ด๋ฏธ ํ™œ์„ฑํ™”๋จ')) + print('SKIP T3 ์ฒดํ—˜ ๋ผ์ด์„ ์Šค ๋ฐœ๊ธ‰: ์ด๋ฏธ ํ™œ์„ฑํ™”๋จ') + +# T4 ํ™œ์„ฑํ™” ํ›„ ์ƒํƒœ +def t4(): + r,_ = api('GET','/api/license/status',token=TOKEN) + return f"valid={r.get('valid')}, edition={r.get('edition')}, days={r.get('days_remaining')}" +test('T4 ํ™œ์„ฑํ™” ํ›„ ์ƒํƒœ ํ™•์ธ', t4) + +# T5 ์ด๋ ฅ ์กฐํšŒ +def t5(): + r,s = api('GET','/api/license/history',token=TOKEN) + cnt = len(r) if isinstance(r,list) else '?' + return f'HTTP {s}: {cnt}๊ฑด' +test('T5 ๋ผ์ด์„ ์Šค ์ด๋ ฅ ์กฐํšŒ', t5) + +# T6 ์ž˜๋ชป๋œ ํ‚ค ๊ฒ€์ฆ +def t6(): + r,s = api('POST','/api/license/verify',{'license_key':'invalid_test_key'},token=TOKEN) + return f'HTTP {s}: {r.get("detail",r.get("message","?"))[:50]}' +test('T6 ์ž˜๋ชป๋œ ํ‚ค ๊ฒ€์ฆ (400/422 ์˜ˆ์ƒ)', t6) + +# T7 Manager UI +import urllib.request as ur +try: + resp2 = ur.urlopen('http://localhost:8090/', timeout=5) + ui_status = resp2.status +except Exception as ex: + ui_status = str(ex) +test('T7 Manager UI ์ ‘์†', lambda: f'HTTP {ui_status}') + +# T8 Manager API +try: + resp3 = ur.urlopen('http://localhost:8002/health', timeout=5) + api_status = json.loads(resp3.read()).get('status','?') +except Exception as ex: + api_status = str(ex) +test('T8 Manager Backend', lambda: api_status) + +# ๊ฒฐ๊ณผ ์š”์•ฝ +print(f'\n{"="*55}') +passed = sum(1 for _,ok,_ in RESULTS if ok is True or ok=='SKIP') +print(f'ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ: {passed}/{len(RESULTS)} PASS') +for name,ok,detail in RESULTS: + icon = 'โœ…' if ok is True else ('โญ๏ธ' if ok=='SKIP' else 'โŒ') + print(f' {icon} {name}') +print('='*55) +""" + +sftp = client.open_sftp() +with sftp.open('/tmp/test_license.py', 'w') as f: + f.write(test_script) +sftp.close() + +ssh('๋ผ์ด์„ ์Šค ํ…Œ์ŠคํŠธ ์‹คํ–‰', 'python3 /tmp/test_license.py 2>&1', timeout=30) +client.close() diff --git a/deploy/test_notification.py b/deploy/test_notification.py new file mode 100644 index 0000000..9194569 --- /dev/null +++ b/deploy/test_notification.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +""" +GUARDiA ITSM โ†’ GUARDiA Messenger ์•Œ๋ฆผ ์†ก์ˆ˜์‹  ํ…Œ์ŠคํŠธ + +ํ…Œ์ŠคํŠธ ํ•ญ๋ชฉ: +T1. ๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ (JWT ๋ฐœ๊ธ‰) +T2. WebSocket ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ (ws://localhost:8001/ws/events) +T3. SR ๋“ฑ๋ก โ†’ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ํ™•์ธ +T4. ์•Œ๋ฆผ ๋กœ๊ทธ ์กฐํšŒ (REST API) +T5. ๋ฉ”์‹ ์ € ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ ์—”๋“œํฌ์ธํŠธ +T6. Messenger ์•ฑ ์•Œ๋ฆผ API ์กฐํšŒ +""" +import paramiko, time, sys, json + +HOST = '101.79.17.164'; USER = 'root'; PASS = '1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +def run(label, cmd, timeout=20): + print(f'\n[{label}]') + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd) + start = time.time() + while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time()-start > timeout: print('[TIMEOUT]'); break + time.sleep(0.3) + while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + chan.recv_exit_status() + +test_script = """ +import urllib.request, urllib.error, json, base64, time, sys + +BASE = 'http://localhost:8001' +RESULTS = [] + +def api(method, path, data=None, token=None, form=False): + url = BASE + path + if form: + import urllib.parse + body = urllib.parse.urlencode(data).encode() if data else None + ct = 'application/x-www-form-urlencoded' + else: + body = json.dumps(data).encode() if data else None + ct = 'application/json' + req = urllib.request.Request(url, data=body, method=method) + req.add_header('Content-Type', ct) + if token: req.add_header('Authorization', 'Bearer '+token) + try: + r = urllib.request.urlopen(req, timeout=10) + return json.loads(r.read()), r.status + except urllib.error.HTTPError as e: + try: return json.loads(e.read()), e.code + except: return {'error': str(e)}, e.code + +def test(name, fn): + try: + r = fn(); ok = True + except Exception as e: + r = str(e); ok = False + RESULTS.append((name, ok, str(r)[:100])) + icon = 'PASS' if ok else 'FAIL' + print(f' [{icon}] {name}: {str(r)[:80]}') + return r if ok else None + +print() +print('='*60) +print('GUARDiA ์•Œ๋ฆผ ์†ก์ˆ˜์‹  ํ…Œ์ŠคํŠธ') +print('='*60) + +# T1: ๋กœ๊ทธ์ธ +d, s = api('POST', '/api/auth/login', {'username':'admin','password':'1111'}) +TOKEN = d.get('access_token','') +test('T1 ๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ', lambda: f'HTTP {s}, Token={TOKEN[:15]}...' if TOKEN else f'FAIL: {d}') + +if not TOKEN: + print('ํ† ํฐ ์—†์Œ - ํ…Œ์ŠคํŠธ ์ค‘๋‹จ') + sys.exit(1) + +# T2: WebSocket ์ƒํƒœ ํ™•์ธ +def t2(): + d, s = api('GET', '/api/ws/status', token=TOKEN) + return f'HTTP {s}, connections={d.get(\"connection_count\",d)}' +test('T2 WebSocket ์ƒํƒœ', t2) + +# T3: SR ๋“ฑ๋ก (์ด๋ฒคํŠธ ๋ฐœ์ƒ) +def t3(): + import secrets + sr_id = f'TEST-{secrets.token_hex(3).upper()}' + d, s = api('POST', '/api/tasks', { + 'title': f'[์•Œ๋ฆผ ํ…Œ์ŠคํŠธ] SR {sr_id}', + 'description': 'GUARDiA Messenger ์•Œ๋ฆผ ์—ฐ๋™ ํ…Œ์ŠคํŠธ์šฉ SR', + 'priority': 'MEDIUM', + 'sr_type': 'OTHER', + }, token=TOKEN) + return f'HTTP {s}, sr_id={d.get(\"sr_id\",d.get(\"detail\",\"?\"))}' +test('T3 SR ๋“ฑ๋ก (์ด๋ฒคํŠธ ํŠธ๋ฆฌ๊ฑฐ)', t3) + +# T4: ์•Œ๋ฆผ ๋กœ๊ทธ ์กฐํšŒ +def t4(): + d, s = api('GET', '/api/notifications/log?size=5', token=TOKEN) + cnt = len(d) if isinstance(d,list) else d.get('total',d.get('count','?')) + items = d[:2] if isinstance(d,list) else d.get('items',d.get('content',[]))[:2] + return f'HTTP {s}, count={cnt}' +test('T4 ์•Œ๋ฆผ ๋กœ๊ทธ ์กฐํšŒ', t4) + +# T5: ๋ฉ”์‹ ์ € ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ +def t5(): + d, s = api('POST', '/api/notifications/test-messenger', token=TOKEN) + return f'HTTP {s}: {d.get(\"message\",d)}' +test('T5 ๋ฉ”์‹ ์ € ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ', t5) + +# T6: SSE ์ด๋ฒคํŠธ ๋ฒ„์Šค ์ƒํƒœ +def t6(): + d, s = api('GET', '/api/dashboard', token=TOKEN) + return f'HTTP {s}: dashboard ok, keys={list(d.keys())[:3]}' +test('T6 ๋Œ€์‹œ๋ณด๋“œ API (์•ฑ ์—ฐ๋™ ํ™•์ธ)', t6) + +# T7: ์•Œ๋ฆผ ์„ค์ • ์กฐํšŒ +def t7(): + d, s = api('GET', '/api/notifications/config', token=TOKEN) + return f'HTTP {s}: smtp={d.get(\"smtp_enabled\",\"?\")}, messenger={d.get(\"messenger_enabled\",\"?\")}' +test('T7 ์•Œ๋ฆผ ์„ค์ • ์กฐํšŒ', t7) + +print() +print('='*60) +passed = sum(1 for _,ok,_ in RESULTS if ok) +print(f'๊ฒฐ๊ณผ: {passed}/{len(RESULTS)} PASS') +for name,ok,detail in RESULTS: + print(f' {\"OK \" if ok else \"FAIL\"} {name}') +print('='*60) + +print() +print('[์•ฑ ์—ฐ๋™ ํ™•์ธ]') +print('GUARDiA Messenger ์•ฑ์—์„œ:') +print('1. ๋กœ๊ทธ์ธ โ†’ ์•Œ๋ฆผ ํƒญ ํƒญ') +print('2. ์ƒ๋‹จ์— "GUARDiA ์‹ค์‹œ๊ฐ„ ์—ฐ๊ฒฐ" ์ดˆ๋ก ํ‘œ์‹œ ํ™•์ธ') +print('3. SR ๋“ฑ๋ก ๋˜๋Š” ๋ฐฐํฌ ์‹คํ–‰ ์‹œ ์•Œ๋ฆผ ์ž๋™ ํ‘œ์‹œ') +print('4. โšก ์‹ค์‹œ๊ฐ„ ํƒœ๊ทธ๋กœ WebSocket ์ˆ˜์‹  ๊ตฌ๋ถ„') +""" + +with sftp.open('/tmp/test_notif.py', 'w') as f: + f.write(test_script) +sftp.close() + +run('์•Œ๋ฆผ ํ…Œ์ŠคํŠธ ์‹คํ–‰', 'python3 /tmp/test_notif.py 2>&1', 30) +client.close() diff --git a/deploy/test_trial.py b/deploy/test_trial.py new file mode 100644 index 0000000..c32a902 --- /dev/null +++ b/deploy/test_trial.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +import paramiko, time, sys + +HOST = '101.79.17.164'; USER = 'root'; PASS = '1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +script = """ +import urllib.request, json + +BASE = "http://localhost:8001" + +def api(method, path, data=None, token=None, form=False): + url = f"{BASE}{path}" + if form: + import urllib.parse + body = urllib.parse.urlencode(data).encode() if data else None + ct = "application/x-www-form-urlencoded" + else: + body = json.dumps(data).encode() if data else None + ct = "application/json" + req = urllib.request.Request(url, data=body, method=method) + req.add_header("Content-Type", ct) + if token: req.add_header("Authorization", f"Bearer {token}") + try: + resp = urllib.request.urlopen(req, timeout=10) + body_out = resp.read() + return json.loads(body_out) if body_out else {}, resp.status + except urllib.error.HTTPError as e: + body_out = e.read() + try: return json.loads(body_out), e.code + except: return {"raw": body_out.decode()[:200]}, e.code + +# JSON ๋กœ๊ทธ์ธ +d, s = api("POST", "/api/auth/login", {"username":"admin","password":"1111"}) +TOKEN = d.get("access_token","") +print(f"Login HTTP {s}: {'OK' if TOKEN else 'FAIL'} token={TOKEN[:20]}...") + +# Trial +d2, s2 = api("POST", "/api/license/trial", {"customer":"์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ์ฒดํ—˜ํŒ","days":7}, token=TOKEN) +print(f"Trial HTTP {s2}: {json.dumps(d2, ensure_ascii=False)[:200]}") + +# ์ƒํƒœ +d3, s3 = api("GET", "/api/license/status", token=TOKEN) +print(f"Status HTTP {s3}: valid={d3.get('valid')}, edition={d3.get('edition')}, days={d3.get('days_remaining')}") +print(f" message: {d3.get('message')}") + +# ์ด๋ ฅ +d4, s4 = api("GET", "/api/license/history", token=TOKEN) +cnt = len(d4) if isinstance(d4,list) else "?" +print(f"History HTTP {s4}: {cnt}๊ฑด") +if isinstance(d4,list) and d4: + r = d4[0] + print(f" ์ตœ์‹ : edition={r.get('edition')}, customer={r.get('customer')}, is_trial={r.get('is_trial')}") +""" + +with sftp.open('/tmp/trial2.py', 'w') as f: f.write(script) +sftp.close() + +chan = client.get_transport().open_session() +chan.set_combine_stderr(True) +chan.exec_command('python3 /tmp/trial2.py 2>&1') +start = time.time() +while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time() - start > 20: break + time.sleep(0.2) +while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) +sys.stdout.flush() +chan.recv_exit_status() +client.close() diff --git a/deploy/test_work_order.py b/deploy/test_work_order.py new file mode 100644 index 0000000..cdd2a9a --- /dev/null +++ b/deploy/test_work_order.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python3 +""" +Messenger โ†’ ITSM ์ž‘์—…์ง€์‹œ ์†ก์ˆ˜์‹  ํ…Œ์ŠคํŠธ + +ํ๋ฆ„: +1. Messenger ์•ฑ์—์„œ SR(์ž‘์—…์ง€์‹œ) ๋“ฑ๋ก +2. ITSM์—์„œ SR ์ƒํƒœ ๋ณ€๊ฒฝ (์Šน์ธ โ†’ ์ง„ํ–‰ โ†’ ์™„๋ฃŒ) +3. Messenger ์•ฑ์œผ๋กœ ์ƒํƒœ ๋ณ€๊ฒฝ ์•Œ๋ฆผ ์ˆ˜์‹  (WebSocket) +4. AI ์ฑ—๋ด‡์œผ๋กœ ์ž์—ฐ์–ด ์ž‘์—…์ง€์‹œ โ†’ SR ์ž๋™ ์ƒ์„ฑ +""" +import paramiko, time, sys + +HOST='101.79.17.164'; USER='root'; PASS='1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +script = r""" +import http.client, json, sys, time, secrets + +HOST = 'localhost'; PORT = 8001 +RESULTS = [] +SR_ID = None # ํ…Œ์ŠคํŠธ์—์„œ ์ƒ์„ฑํ•œ SR ID ์ €์žฅ + +def api(method, path, data=None, token=None): + conn = http.client.HTTPConnection(HOST, PORT, timeout=10) + headers = {'Content-Type':'application/json'} + if token: headers['Authorization'] = 'Bearer ' + token + body = json.dumps(data).encode() if data else None + conn.request(method, path, body=body, headers=headers) + r = conn.getresponse(); raw = r.read() + try: return json.loads(raw), r.status + except: return {'raw': raw.decode()[:300]}, r.status + +def test(name, fn): + try: + r = fn(); ok = True + except Exception as e: r = str(e)[:100]; ok = False + RESULTS.append((name, ok, str(r)[:100])) + print(('PASS' if ok else 'FAIL') + ' ' + name + ': ' + str(r)[:90]) + return r if ok else None + +print() +print('='*65) +print('Messenger -> ITSM ์ž‘์—…์ง€์‹œ ์†ก์ˆ˜์‹  ํ…Œ์ŠคํŠธ') +print('='*65) + +# ๋กœ๊ทธ์ธ +d, s = api('POST','/api/auth/login',{'username':'admin','password':'1111'}) +TOKEN = d.get('access_token','') +if not TOKEN: print('๋กœ๊ทธ์ธ ์‹คํŒจ'); sys.exit(1) +print(f' ๋กœ๊ทธ์ธ OK (token={TOKEN[:15]}...)') +print() + +# โ”€โ”€ ๋‹จ๊ณ„ 1: Messenger ์•ฑ์—์„œ ์ž‘์—…์ง€์‹œ ๋“ฑ๋ก โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +print('[๋‹จ๊ณ„ 1] Messenger ์•ฑ โ†’ ITSM ์ž‘์—…์ง€์‹œ ๋“ฑ๋ก') + +suffix = secrets.token_hex(3).upper() + +def step1_sr(): + global SR_ID + # Messenger ์•ฑ์˜ SR ๋“ฑ๋ก ํ™”๋ฉด์—์„œ ์ง์ ‘ POST + d, s = api('POST','/api/tasks', { + 'title': f'[Messenger] ์›น์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ์š”์ฒญ {suffix}', + 'description': 'GUARDiA Messenger ์•ฑ์—์„œ ์ง์ ‘ ๋“ฑ๋กํ•œ ์ž‘์—…์ง€์‹œ์ž…๋‹ˆ๋‹ค.\n' + '์„œ๋ฒ„: web-01.zioinfo.co.kr\n' + '์ž‘์—…: nginx ์žฌ์‹œ์ž‘\n' + '์š”์ฒญ์ž: admin (Messenger ์•ฑ)', + 'priority': 'HIGH', + 'sr_type': 'RESTART', + 'requested_by': 'admin', + }, token=TOKEN) + if s in [200,201]: + SR_ID = d.get('id') or d.get('sr_id') + return f'HTTP {s} SR๋“ฑ๋ก ์„ฑ๊ณต: id={d.get("id")} sr_id={d.get("sr_id","?")} title={d.get("title","?")[:30]}' + return f'HTTP {s}: {d.get("detail","?")[:80]}' + +test('S1-1 Messengerโ†’ITSM SR ๋“ฑ๋ก (POST /api/tasks)', step1_sr) + +# SR ๋ชฉ๋ก์—์„œ ๋ฐฉ๊ธˆ ์ƒ์„ฑํ•œ SR ํ™•์ธ +def step1_verify(): + d, s = api('GET','/api/tasks?size=1',token=TOKEN) + if isinstance(d,list) and len(d)>0: + latest = d[0] + return f'HTTP {s} ์ตœ์‹ SR: {latest.get("sr_id","?")} [{latest.get("status","?")}] {latest.get("title","?")[:30]}' + return f'HTTP {s} {type(d)}' +test('S1-2 ITSM SR ๋ชฉ๋ก ํ™•์ธ', step1_verify) + +print() + +# โ”€โ”€ ๋‹จ๊ณ„ 2: ITSM์—์„œ SR ์ƒํƒœ ๋ณ€๊ฒฝ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +print('[๋‹จ๊ณ„ 2] ITSM SR ์ƒํƒœ ๋ณ€๊ฒฝ โ†’ Messenger ์•Œ๋ฆผ ์ˆ˜์‹ ') + +def step2_approve(): + # SR์„ APPROVED ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ (์Šน์ธ) + if not SR_ID: return 'SR_ID ์—†์Œ' + # PATCH /api/tasks/{id}/status ๋˜๋Š” PUT + d, s = api('PATCH', f'/api/tasks/{SR_ID}/status', {'status':'APPROVED'}, token=TOKEN) + if s in [200,201]: + return f'HTTP {s} ์ƒํƒœ๋ณ€๊ฒฝ: APPROVED -> WebSocket ์•Œ๋ฆผ ๋ฐœ์ƒ' + # ๋‹ค๋ฅธ ๊ฒฝ๋กœ ์‹œ๋„ + d2, s2 = api('PUT', f'/api/tasks/{SR_ID}', {'status':'IN_PROGRESS'}, token=TOKEN) + return f'HTTP {s} / {s2}: {d.get("detail","?")} / {d2.get("status","?")}' +test('S2-1 SR ์Šน์ธ (APPROVED) โ†’ ์•ฑ ์•Œ๋ฆผ ์ˆ˜์‹ ', step2_approve) + +def step2_inprogress(): + if not SR_ID: return 'SR_ID ์—†์Œ' + d, s = api('PATCH', f'/api/tasks/{SR_ID}/status', {'status':'IN_PROGRESS'}, token=TOKEN) + if s in [200,201]: + return f'HTTP {s} ์ƒํƒœ๋ณ€๊ฒฝ: IN_PROGRESS -> ์•ฑ์— ์ง„ํ–‰์ค‘ ์•Œ๋ฆผ' + return f'HTTP {s}: {d.get("detail","?")[:60]}' +test('S2-2 SR ์ง„ํ–‰ (IN_PROGRESS) โ†’ ์•ฑ ์•Œ๋ฆผ ์ˆ˜์‹ ', step2_inprogress) + +def step2_complete(): + if not SR_ID: return 'SR_ID ์—†์Œ' + d, s = api('PATCH', f'/api/tasks/{SR_ID}/status', {'status':'COMPLETED'}, token=TOKEN) + if s in [200,201]: + return f'HTTP {s} ์ƒํƒœ๋ณ€๊ฒฝ: COMPLETED -> ์•ฑ์— ์™„๋ฃŒ ์•Œ๋ฆผ' + return f'HTTP {s}: {d.get("detail","?")[:60]}' +test('S2-3 SR ์™„๋ฃŒ (COMPLETED) โ†’ ์•ฑ ์•Œ๋ฆผ ์ˆ˜์‹ ', step2_complete) + +print() + +# โ”€โ”€ ๋‹จ๊ณ„ 3: AI ์ฑ—๋ด‡์œผ๋กœ ์ž‘์—…์ง€์‹œ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +print('[๋‹จ๊ณ„ 3] Messenger AI ์ฑ—๋ด‡ โ†’ ์ž์—ฐ์–ด ์ž‘์—…์ง€์‹œ') + +def step3_ai_cmd(): + d, s = api('POST','/api/chatbot/message', + {'message': '์›น์„œ๋ฒ„ nginx๋ฅผ ์žฌ์‹œ์ž‘ํ•ด ์ฃผ์„ธ์š”. ๊ธด๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.'}, + token=TOKEN) + reply = d.get('reply', d.get('message', d.get('response', str(d)[:80]))) + return f'HTTP {s} AI์‘๋‹ต: {str(reply)[:80]}' +test('S3-1 AI์ฑ—๋ด‡ ์ž์—ฐ์–ด ์ž‘์—…์ง€์‹œ (nginx ์žฌ์‹œ์ž‘)', step3_ai_cmd) + +def step3_ai_sr(): + d, s = api('POST','/api/chatbot/message', + {'message': 'SR ๋“ฑ๋กํ•ด์ค˜: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฑ์—… ์‹คํ–‰ ์š”์ฒญ, ์šฐ์„ ์ˆœ์œ„ HIGH'}, + token=TOKEN) + reply = d.get('reply', d.get('message', str(d)[:80])) + return f'HTTP {s} AI์‘๋‹ต: {str(reply)[:80]}' +test('S3-2 AI์ฑ—๋ด‡ SR ์ž๋™ ์ƒ์„ฑ ์š”์ฒญ', step3_ai_sr) + +def step3_nlcmd(): + # ์ž์—ฐ์–ด ๋ช…๋ น (nlcmd) + d, s = api('POST','/api/ai-cmd', + {'message': 'ํ˜„์žฌ ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ํ™•์ธํ•ด์ค˜', 'channel': 'messenger'}, + token=TOKEN) + return f'HTTP {s}: {str(d)[:80]}' +test('S3-3 AI ์ž์—ฐ์–ด ๋ช…๋ น (์„œ๋ฒ„ ์ƒํƒœ ํ™•์ธ)', step3_nlcmd) + +print() + +# โ”€โ”€ ๋‹จ๊ณ„ 4: WebSocket ์ด๋ฒคํŠธ ์ง์ ‘ ํ™•์ธ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +print('[๋‹จ๊ณ„ 4] WebSocket ์ด๋ฒคํŠธ ์ƒํƒœ ํ™•์ธ') + +def step4_ws(): + d, s = api('GET','/api/ws/status',token=TOKEN) + conns = d.get('total_connections', d.get('connection_count', 0)) + channels = d.get('channels', []) + return f'HTTP {s} ์—ฐ๊ฒฐ={conns}๊ฐœ ์ฑ„๋„={channels[:4]}' +test('S4-1 WebSocket ์—ฐ๊ฒฐ ์ƒํƒœ (์•ฑ ์—ฐ๊ฒฐ ์ˆ˜)', step4_ws) + +def step4_audit(): + d, s = api('GET','/api/audit?size=5',token=TOKEN) + items = d if isinstance(d,list) else d.get('items',d.get('content',[])) + logs = [(i.get('action','?'), i.get('username','?')) for i in items[:3]] + return f'HTTP {s} ์ตœ๊ทผ๊ฐ์‚ฌ: {logs}' +test('S4-2 ๊ฐ์‚ฌ ๋กœ๊ทธ (์ž‘์—…์ง€์‹œ ์ด๋ ฅ ์ถ”์ )', step4_audit) + +print() +print('='*65) +passed = sum(1 for _,ok,_ in RESULTS if ok) +print(f'๊ฒฐ๊ณผ: {passed}/{len(RESULTS)} PASS') +print() +for name,ok,d in RESULTS: + print(f' {"OK " if ok else "FAIL"} {name}') +print() +print('='*65) +print() +print('[์ž‘์—…์ง€์‹œ ์ „์ฒด ํ๋ฆ„]') +print() +print(' Messenger ์•ฑ GUARDiA ITSM') +print(' | |') +print(' SR ๋“ฑ๋ก ํƒญ์—์„œ ์ž…๋ ฅ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€> POST /api/tasks') +print(' | |') +print(' AI์ฑ—๋ด‡: "nginx ์žฌ์‹œ์ž‘ํ•ด์ค˜" โ”€โ”€โ”€โ”€โ”€> /api/chatbot/message') +print(' | | (AI๊ฐ€ SR ์ž๋™ ์ƒ์„ฑ)') +print(' | <โ”€โ”€ WebSocket ์ƒํƒœ ์•Œ๋ฆผ โ”€โ”€โ”€โ”€ ๊ด€๋ฆฌ์ž ์Šน์ธ') +print(' | <โ”€โ”€ WebSocket ์™„๋ฃŒ ์•Œ๋ฆผ โ”€โ”€โ”€โ”€ ์ž‘์—… ์™„๋ฃŒ ์ฒ˜๋ฆฌ') +print() +print('[์•ฑ์—์„œ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•]') +print('1. ์•ฑ SRํƒญ -> "์ƒˆ SR" ๋ฒ„ํŠผ -> ์ œ๋ชฉ/์„ค๋ช…/์šฐ์„ ์ˆœ์œ„ ์ž…๋ ฅ -> ๋“ฑ๋ก') +print(' โ†’ ITSM ๊ด€๋ฆฌ์ž ์›น(http://101.79.17.164:8001)์—์„œ SR ํ™•์ธ') +print() +print('2. ์•ฑ AI์ฑ„ํŒ…ํƒญ -> "์„œ๋ฒ„ nginx ์žฌ์‹œ์ž‘ ์š”์ฒญํ•ด์ค˜" ์ž…๋ ฅ') +print(' โ†’ AI๊ฐ€ ์ž๋™์œผ๋กœ SR ์ƒ์„ฑ ํ›„ ์‘๋‹ต') +print() +print('3. ์•ฑ ์•Œ๋ฆผํƒญ -> โšก ์‹ค์‹œ๊ฐ„ ์—ฐ๊ฒฐ ์ดˆ๋ก ํ‘œ์‹œ ํ™•์ธ') +print(' โ†’ ITSM์—์„œ SR ์ƒํƒœ ๋ณ€๊ฒฝ ์‹œ ์ฆ‰์‹œ ์•Œ๋ฆผ ์ˆ˜์‹ ') +""" + +with sftp.open('/tmp/test_wo.py','w') as f: f.write(script) +sftp.close() + +chan = client.get_transport().open_session() +chan.set_combine_stderr(True) +chan.exec_command('python3 /tmp/test_wo.py 2>&1') +start = time.time() +while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(8192)); sys.stdout.flush() + if time.time()-start > 50: break + time.sleep(0.3) +while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(8192)) +sys.stdout.flush() +chan.recv_exit_status() +client.close() diff --git a/deploy/update_deploy_server.py b/deploy/update_deploy_server.py new file mode 100644 index 0000000..4d63ed2 --- /dev/null +++ b/deploy/update_deploy_server.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +"""Deploy Webhook ์„œ๋ฒ„์— guardia-itsm ๋ฐฐํฌ ์Šคํ… ์ถ”๊ฐ€""" +import paramiko, time, sys + +HOST='101.79.17.164'; USER='root'; PASS='1q2w3e!Q' +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect(HOST, username=USER, password=PASS, timeout=15) +sftp = client.open_sftp() + +def run(label, cmd, timeout=30): + print(f'[{label}]') + chan = client.get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd) + start = time.time() + while not chan.exit_status_ready(): + if chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)); sys.stdout.flush() + if time.time()-start > timeout: break + time.sleep(0.3) + while chan.recv_ready(): sys.stdout.buffer.write(chan.recv(4096)) + sys.stdout.flush() + chan.recv_exit_status() + +new_deploy_server = '''#!/usr/bin/env python3 +""" +ZioInfo + GUARDiA CI/CD Webhook Server +ํฌํŠธ: 9999 +- zioinfo-web push โ†’ ํ™ˆํŽ˜์ด์ง€ ๋นŒ๋“œ/๋ฐฐํฌ +- guardia-itsm push โ†’ GUARDiA ITSM ์žฌ๋ฐฐํฌ +""" +import http.server, subprocess, threading, json, hmac, hashlib, logging, os + +SECRET = b"zioinfo-deploy-2026" +ZIOINFO_SRC = "/opt/zioinfo/src" +GUARDIA_SRC = "/opt/guardia/src" +LOG = "/var/log/zioinfo/deploy.log" +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(levelname)s %(message)s", + handlers=[logging.FileHandler(LOG), logging.StreamHandler()]) + +def deploy_zioinfo(): + logging.info("=== zioinfo-web ๋ฐฐํฌ ์‹œ์ž‘ ===") + steps = [ + ("git pull", ["git", "-C", ZIOINFO_SRC, "pull", "origin", "main"]), + ("npm build", ["bash", "-c", f"cd {ZIOINFO_SRC}/frontend && npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps && npm run build"]), + ("mvn package", ["bash", "-c", f"cd {ZIOINFO_SRC}/backend && /usr/bin/mvn clean package -DskipTests -q"]), + ("deploy jar", ["bash", "-c", f"cp {ZIOINFO_SRC}/backend/target/zioinfo-web-*.jar /opt/zioinfo/app/app.jar"]), + ("deploy static", ["bash", "-c", f"cp -r {ZIOINFO_SRC}/backend/src/main/resources/static/. /var/www/zioinfo/"]), + ("restart", ["systemctl", "restart", "zioinfo"]), + ] + for name, cmd in steps: + logging.info(f"[{name}] ์‹คํ–‰ ์ค‘...") + r = subprocess.run(cmd, capture_output=True, text=True, timeout=600) + if r.returncode != 0: + logging.error(f"[{name}] ์‹คํŒจ: {r.stderr[-300:]}") + return False + logging.info(f"[{name}] ์™„๋ฃŒ") + logging.info("=== zioinfo-web ๋ฐฐํฌ ์™„๋ฃŒ ===") + return True + +def deploy_guardia(): + logging.info("=== GUARDiA ITSM ๋ฐฐํฌ ์‹œ์ž‘ ===") + steps = [ + ("git clone/pull", ["bash", "-c", + f"[ -d {GUARDIA_SRC}/.git ] && git -C {GUARDIA_SRC} pull origin main " + f"|| git clone http://zio:Zio%40Admin2026%21@localhost:3000/zio/guardia-itsm.git {GUARDIA_SRC}"]), + ("rsync", ["bash", "-c", + f"rsync -a --exclude=__pycache__ --exclude=*.db --exclude=.git " + f"{GUARDIA_SRC}/ /opt/guardia/app/"]), + ("pip install", ["/opt/guardia/venv/bin/pip", "install", "-r", + "/opt/guardia/app/requirements.txt", "-q"]), + ("restart", ["systemctl", "restart", "guardia"]), + ] + for name, cmd in steps: + logging.info(f"[{name}] ์‹คํ–‰ ์ค‘...") + r = subprocess.run(cmd, capture_output=True, text=True, timeout=300) + if r.returncode != 0: + logging.error(f"[{name}] ์‹คํŒจ: {r.stderr[-300:]}") + return False + logging.info(f"[{name}] ์™„๋ฃŒ") + logging.info("=== GUARDiA ITSM ๋ฐฐํฌ ์™„๋ฃŒ ===") + return True + +class WebhookHandler(http.server.BaseHTTPRequestHandler): + def do_POST(self): + length = int(self.headers.get("Content-Length", 0)) + body = self.rfile.read(length) + sig = self.headers.get("X-Gitea-Signature", "") + expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest() + if sig and not hmac.compare_digest(sig, expected): + self.send_response(403); self.end_headers(); return + self.send_response(202); self.end_headers() + self.wfile.write(b"Deploy queued") + # ์–ด๋–ค ์ €์žฅ์†Œ์ธ์ง€ ํŒ๋‹จ + try: + payload = json.loads(body) + repo = payload.get("repository", {}).get("name", "") + except Exception: + repo = "" + if "guardia" in repo.lower(): + threading.Thread(target=deploy_guardia, daemon=True).start() + else: + threading.Thread(target=deploy_zioinfo, daemon=True).start() + def log_message(self, fmt, *args): + logging.info(fmt % args) + +if __name__ == "__main__": + os.makedirs("/var/log/zioinfo", exist_ok=True) + logging.info("ZioInfo + GUARDiA Deploy Webhook ์„œ๋ฒ„ ์‹œ์ž‘ (ํฌํŠธ 9999)") + server = http.server.HTTPServer(("0.0.0.0", 9999), WebhookHandler) + server.serve_forever() +''' + +with sftp.open('/opt/zioinfo/deploy_server.py', 'w') as f: + f.write(new_deploy_server) +sftp.close() + +run('Deploy ์„œ๋ฒ„ ์žฌ์‹œ์ž‘', 'systemctl restart zioinfo-deploy && sleep 3 && systemctl is-active zioinfo-deploy') +run('Webhook ํ…Œ์ŠคํŠธ', 'curl -s -X POST http://localhost:9999/ -d "{}" -w " HTTP %{http_code}"') + +client.close() +print('์™„๋ฃŒ: Deploy Webhook ์„œ๋ฒ„ ์—…๋ฐ์ดํŠธ (zioinfo + guardia ๋ชจ๋‘ ์ง€์›)') diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..fe3b9bc --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,21 @@ + + + + + + + + + + + (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  + + + + + + +
+ + + diff --git a/frontend/public/screenshots/01_home.png b/frontend/public/screenshots/01_home.png new file mode 100644 index 0000000..9dd704f Binary files /dev/null and b/frontend/public/screenshots/01_home.png differ diff --git a/frontend/public/screenshots/01_home_viewport.png b/frontend/public/screenshots/01_home_viewport.png new file mode 100644 index 0000000..64e7d88 Binary files /dev/null and b/frontend/public/screenshots/01_home_viewport.png differ diff --git a/frontend/public/screenshots/02_guardia.png b/frontend/public/screenshots/02_guardia.png new file mode 100644 index 0000000..df6d4c7 Binary files /dev/null and b/frontend/public/screenshots/02_guardia.png differ diff --git a/frontend/public/screenshots/02_guardia_viewport.png b/frontend/public/screenshots/02_guardia_viewport.png new file mode 100644 index 0000000..19b5add Binary files /dev/null and b/frontend/public/screenshots/02_guardia_viewport.png differ diff --git a/frontend/public/screenshots/03_company.png b/frontend/public/screenshots/03_company.png new file mode 100644 index 0000000..88e925d Binary files /dev/null and b/frontend/public/screenshots/03_company.png differ diff --git a/frontend/public/screenshots/03_company_viewport.png b/frontend/public/screenshots/03_company_viewport.png new file mode 100644 index 0000000..e64b817 Binary files /dev/null and b/frontend/public/screenshots/03_company_viewport.png differ diff --git a/frontend/public/screenshots/04_contact.png b/frontend/public/screenshots/04_contact.png new file mode 100644 index 0000000..6bfc0eb Binary files /dev/null and b/frontend/public/screenshots/04_contact.png differ diff --git a/frontend/public/screenshots/04_contact_viewport.png b/frontend/public/screenshots/04_contact_viewport.png new file mode 100644 index 0000000..f456b4e Binary files /dev/null and b/frontend/public/screenshots/04_contact_viewport.png differ diff --git a/frontend/public/screenshots/05_news.png b/frontend/public/screenshots/05_news.png new file mode 100644 index 0000000..48dc214 Binary files /dev/null and b/frontend/public/screenshots/05_news.png differ diff --git a/frontend/public/screenshots/05_news_viewport.png b/frontend/public/screenshots/05_news_viewport.png new file mode 100644 index 0000000..f212eb2 Binary files /dev/null and b/frontend/public/screenshots/05_news_viewport.png differ diff --git a/frontend/public/screenshots/06_mobile_home.png b/frontend/public/screenshots/06_mobile_home.png new file mode 100644 index 0000000..c3f3355 Binary files /dev/null and b/frontend/public/screenshots/06_mobile_home.png differ diff --git a/frontend/public/screenshots/all-pages-result.json b/frontend/public/screenshots/all-pages-result.json new file mode 100644 index 0000000..49a94b3 --- /dev/null +++ b/frontend/public/screenshots/all-pages-result.json @@ -0,0 +1,212 @@ +[ + { + "page": "ํ™ˆ", + "url": "/", + "http": 200, + "loadMs": 5849, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "AI ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ์ž์œจ ์šด์˜ ํ”Œ๋žซํผ", + "errors": 0, + "ok": true + }, + { + "page": "GUARDiA ITSM", + "url": "/solution/guardia", + "http": 200, + "loadMs": 1292, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "GUARDiA ITSM", + "errors": 0, + "ok": true + }, + { + "page": "์†”๋ฃจ์…˜-ERP", + "url": "/solution/erp", + "http": 200, + "loadMs": 1767, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "ERP ์†”๋ฃจ์…˜", + "errors": 0, + "ok": true + }, + { + "page": "์†”๋ฃจ์…˜-CRM", + "url": "/solution/crm", + "http": 200, + "loadMs": 1139, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "CRM ์†”๋ฃจ์…˜", + "errors": 0, + "ok": true + }, + { + "page": "์†”๋ฃจ์…˜-BI", + "url": "/solution/bi", + "http": 200, + "loadMs": 966, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "BI ์†”๋ฃจ์…˜", + "errors": 0, + "ok": true + }, + { + "page": "ํšŒ์‚ฌ-CEO์ธ์‚ฌ๋ง", + "url": "/company/greeting", + "http": 200, + "loadMs": 1098, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "CEO ์ธ์‚ฌ๋ง", + "errors": 0, + "ok": true + }, + { + "page": "ํšŒ์‚ฌ-์—ฐํ˜", + "url": "/company/history", + "http": 200, + "loadMs": 1548, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์—ฐํ˜", + "errors": 0, + "ok": true + }, + { + "page": "ํšŒ์‚ฌ-์กฐ์ง๋„", + "url": "/company/organization", + "http": 200, + "loadMs": 892, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์กฐ์ง๋„", + "errors": 0, + "ok": true + }, + { + "page": "ํšŒ์‚ฌ-CI์†Œ๊ฐœ", + "url": "/company/ci", + "http": 200, + "loadMs": 1007, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "CI ์†Œ๊ฐœ", + "errors": 0, + "ok": true + }, + { + "page": "ํšŒ์‚ฌ-์˜ค์‹œ๋Š”๊ธธ", + "url": "/company/location", + "http": 200, + "loadMs": 1070, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์˜ค์‹œ๋Š” ๊ธธ", + "errors": 0, + "ok": true + }, + { + "page": "์‚ฌ์—…-๋ ˆํผ๋Ÿฐ์Šค", + "url": "/business/reference", + "http": 200, + "loadMs": 1111, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "๊ตฌ์ถ• ๋ ˆํผ๋Ÿฐ์Šค", + "errors": 0, + "ok": true + }, + { + "page": "์‚ฌ์—…-ํŒŒํŠธ๋„ˆ", + "url": "/business/partner", + "http": 200, + "loadMs": 1090, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "ํŒŒํŠธ๋„ˆ", + "errors": 0, + "ok": true + }, + { + "page": "์ง€์›-๊ณต์ง€์‚ฌํ•ญ", + "url": "/support/notice", + "http": 200, + "loadMs": 949, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "๊ณต์ง€์‚ฌํ•ญ", + "errors": 0, + "ok": true + }, + { + "page": "์ง€์›-FAQ", + "url": "/support/faq", + "http": 200, + "loadMs": 931, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ", + "errors": 0, + "ok": true + }, + { + "page": "์ง€์›-์นดํƒˆ๋กœ๊ทธ", + "url": "/support/catalog", + "http": 200, + "loadMs": 963, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์นดํƒˆ๋กœ๊ทธ", + "errors": 0, + "ok": true + }, + { + "page": "์ง€์›-๋ฌธ์˜ํ•˜๊ธฐ", + "url": "/support/contact", + "http": 200, + "loadMs": 1007, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "๋ฌธ์˜ํ•˜๊ธฐ", + "errors": 0, + "ok": true + }, + { + "page": "์ฑ„์šฉ-๊ณต๊ณ ", + "url": "/recruit/jobs", + "http": 200, + "loadMs": 984, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์ฑ„์šฉ๊ณต๊ณ ", + "errors": 0, + "ok": true + }, + { + "page": "์ฑ„์šฉ-๋ณต๋ฆฌํ›„์ƒ", + "url": "/recruit/welfare", + "http": 200, + "loadMs": 1275, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "๋ณต๋ฆฌํ›„์ƒ", + "errors": 0, + "ok": true + }, + { + "page": "์ฑ„์šฉ-์ง€์›ํ•˜๊ธฐ", + "url": "/recruit/apply", + "http": 200, + "loadMs": 880, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "์ง€์›ํ•˜๊ธฐ", + "errors": 0, + "ok": true + }, + { + "page": "๋‰ด์Šค-๋‰ด์Šค๋ฃธ", + "url": "/news/newsroom", + "http": 200, + "loadMs": 1144, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "๋‰ด์Šค๋ฃธ", + "errors": 0, + "ok": true + }, + { + "page": "๋‰ด์Šค-๋ธ”๋กœ๊ทธ", + "url": "/news/blog", + "http": 200, + "loadMs": 989, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "h1": "๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ", + "errors": 0, + "ok": true + } +] \ No newline at end of file diff --git a/frontend/public/screenshots/business_partner.png b/frontend/public/screenshots/business_partner.png new file mode 100644 index 0000000..3dce8a2 Binary files /dev/null and b/frontend/public/screenshots/business_partner.png differ diff --git a/frontend/public/screenshots/business_ref.png b/frontend/public/screenshots/business_ref.png new file mode 100644 index 0000000..8b93a0f Binary files /dev/null and b/frontend/public/screenshots/business_ref.png differ diff --git a/frontend/public/screenshots/company_ci.png b/frontend/public/screenshots/company_ci.png new file mode 100644 index 0000000..2eb6f3c Binary files /dev/null and b/frontend/public/screenshots/company_ci.png differ diff --git a/frontend/public/screenshots/company_greeting.png b/frontend/public/screenshots/company_greeting.png new file mode 100644 index 0000000..9f93270 Binary files /dev/null and b/frontend/public/screenshots/company_greeting.png differ diff --git a/frontend/public/screenshots/company_history.png b/frontend/public/screenshots/company_history.png new file mode 100644 index 0000000..bc970cb Binary files /dev/null and b/frontend/public/screenshots/company_history.png differ diff --git a/frontend/public/screenshots/company_location.png b/frontend/public/screenshots/company_location.png new file mode 100644 index 0000000..5132a04 Binary files /dev/null and b/frontend/public/screenshots/company_location.png differ diff --git a/frontend/public/screenshots/company_org.png b/frontend/public/screenshots/company_org.png new file mode 100644 index 0000000..c38e7b0 Binary files /dev/null and b/frontend/public/screenshots/company_org.png differ diff --git a/frontend/public/screenshots/guardia.png b/frontend/public/screenshots/guardia.png new file mode 100644 index 0000000..19b5add Binary files /dev/null and b/frontend/public/screenshots/guardia.png differ diff --git a/frontend/public/screenshots/home.png b/frontend/public/screenshots/home.png new file mode 100644 index 0000000..5b4df9d Binary files /dev/null and b/frontend/public/screenshots/home.png differ diff --git a/frontend/public/screenshots/news_blog.png b/frontend/public/screenshots/news_blog.png new file mode 100644 index 0000000..94697ec Binary files /dev/null and b/frontend/public/screenshots/news_blog.png differ diff --git a/frontend/public/screenshots/news_newsroom.png b/frontend/public/screenshots/news_newsroom.png new file mode 100644 index 0000000..d799d77 Binary files /dev/null and b/frontend/public/screenshots/news_newsroom.png differ diff --git a/frontend/public/screenshots/recruit_apply.png b/frontend/public/screenshots/recruit_apply.png new file mode 100644 index 0000000..8e79f18 Binary files /dev/null and b/frontend/public/screenshots/recruit_apply.png differ diff --git a/frontend/public/screenshots/recruit_jobs.png b/frontend/public/screenshots/recruit_jobs.png new file mode 100644 index 0000000..951e94f Binary files /dev/null and b/frontend/public/screenshots/recruit_jobs.png differ diff --git a/frontend/public/screenshots/recruit_welfare.png b/frontend/public/screenshots/recruit_welfare.png new file mode 100644 index 0000000..53752fb Binary files /dev/null and b/frontend/public/screenshots/recruit_welfare.png differ diff --git a/frontend/public/screenshots/solution_bi.png b/frontend/public/screenshots/solution_bi.png new file mode 100644 index 0000000..6999f9f Binary files /dev/null and b/frontend/public/screenshots/solution_bi.png differ diff --git a/frontend/public/screenshots/solution_crm.png b/frontend/public/screenshots/solution_crm.png new file mode 100644 index 0000000..a45d34d Binary files /dev/null and b/frontend/public/screenshots/solution_crm.png differ diff --git a/frontend/public/screenshots/solution_erp.png b/frontend/public/screenshots/solution_erp.png new file mode 100644 index 0000000..c647195 Binary files /dev/null and b/frontend/public/screenshots/solution_erp.png differ diff --git a/frontend/public/screenshots/support_catalog.png b/frontend/public/screenshots/support_catalog.png new file mode 100644 index 0000000..e9ee299 Binary files /dev/null and b/frontend/public/screenshots/support_catalog.png differ diff --git a/frontend/public/screenshots/support_contact.png b/frontend/public/screenshots/support_contact.png new file mode 100644 index 0000000..f456b4e Binary files /dev/null and b/frontend/public/screenshots/support_contact.png differ diff --git a/frontend/public/screenshots/support_faq.png b/frontend/public/screenshots/support_faq.png new file mode 100644 index 0000000..62006a5 Binary files /dev/null and b/frontend/public/screenshots/support_faq.png differ diff --git a/frontend/public/screenshots/support_notice.png b/frontend/public/screenshots/support_notice.png new file mode 100644 index 0000000..eba6bd0 Binary files /dev/null and b/frontend/public/screenshots/support_notice.png differ diff --git a/frontend/public/screenshots/test-result.json b/frontend/public/screenshots/test-result.json new file mode 100644 index 0000000..04877fa --- /dev/null +++ b/frontend/public/screenshots/test-result.json @@ -0,0 +1,67 @@ +[ + { + "page": "ํ™ˆ", + "url": "/", + "status": 200, + "loadMs": 9745, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "links": 37, + "images": 2, + "h1": 1, + "errors": 0, + "errorMsgs": [], + "screenshot": "C:\\GUARDiA\\workspace\\zioinfo-web\\frontend\\public\\screenshots\\01_home.png" + }, + { + "page": "GUARDiA ์†Œ๊ฐœ", + "url": "/solution/guardia", + "status": 200, + "loadMs": 1130, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "links": 27, + "images": 8, + "h1": 1, + "errors": 0, + "errorMsgs": [], + "screenshot": "C:\\GUARDiA\\workspace\\zioinfo-web\\frontend\\public\\screenshots\\02_guardia.png" + }, + { + "page": "ํšŒ์‚ฌ์†Œ๊ฐœ", + "url": "/company/greeting", + "status": 200, + "loadMs": 971, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "links": 23, + "images": 2, + "h1": 1, + "errors": 0, + "errorMsgs": [], + "screenshot": "C:\\GUARDiA\\workspace\\zioinfo-web\\frontend\\public\\screenshots\\03_company.png" + }, + { + "page": "๋ฌธ์˜ํ•˜๊ธฐ", + "url": "/support/contact", + "status": 200, + "loadMs": 890, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "links": 24, + "images": 2, + "h1": 1, + "errors": 0, + "errorMsgs": [], + "screenshot": "C:\\GUARDiA\\workspace\\zioinfo-web\\frontend\\public\\screenshots\\04_contact.png" + }, + { + "page": "๋‰ด์Šค", + "url": "/news/press", + "status": 200, + "loadMs": 1007, + "title": "(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ", + "links": 23, + "images": 2, + "h1": 1, + "errors": 0, + "errorMsgs": [], + "screenshot": "C:\\GUARDiA\\workspace\\zioinfo-web\\frontend\\public\\screenshots\\05_news.png" + } +] \ No newline at end of file diff --git a/frontend/src/pages/Business.css b/frontend/src/pages/Business.css new file mode 100644 index 0000000..c3272f3 --- /dev/null +++ b/frontend/src/pages/Business.css @@ -0,0 +1,44 @@ +/* ๋ ˆํผ๋Ÿฐ์Šค ํ•„ํ„ฐ */ +.ref-filters { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 24px; } +.ref-filter-btn { + padding: 7px 18px; border-radius: 20px; border: 1px solid var(--gray-200); + font-size: 13px; font-weight: 500; color: var(--gray-600); cursor: pointer; + transition: all var(--fast) var(--ease); background: var(--white); +} +.ref-filter-btn:hover { border-color: var(--primary); color: var(--primary); } +.ref-filter-btn.active { background: var(--primary); border-color: var(--primary); color: #fff; } + +/* ๋ ˆํผ๋Ÿฐ์Šค ํ…Œ์ด๋ธ” */ +.ref-table-wrap { overflow-x: auto; border-radius: 12px; border: 1px solid var(--gray-200); } +.ref-table { width: 100%; border-collapse: collapse; min-width: 800px; } +.ref-table th { + background: var(--secondary); color: rgba(255,255,255,.8); + padding: 14px 16px; text-align: left; font-size: 12px; + font-weight: 600; letter-spacing: .5px; +} +.ref-table td { padding: 13px 16px; font-size: 13px; border-bottom: 1px solid var(--gray-100); vertical-align: middle; } +.ref-table tr:last-child td { border-bottom: none; } +.ref-table tr:hover td { background: var(--gray-50); } +.ref-period { color: var(--gray-500); font-size: 12px; white-space: nowrap; } +.ref-client { font-weight: 700; color: var(--gray-800); white-space: nowrap; } +.ref-project { color: var(--gray-700); } +.ref-role { + padding: 3px 10px; border-radius: 12px; font-size: 11px; font-weight: 700; + background: var(--primary-light); color: var(--primary); white-space: nowrap; +} +.ref-tech { font-size: 12px; color: var(--gray-500); } +.ref-cat-badge { padding: 3px 10px; border-radius: 12px; font-size: 11px; font-weight: 600; white-space: nowrap; } + +/* ํŒŒํŠธ๋„ˆ ์นด๋“œ */ +.partner-card { padding: 32px 24px; text-align: center; } +.partner-logo { font-size: 48px; margin-bottom: 12px; } +.partner-tier { display: inline-block; padding: 3px 12px; border-radius: 12px; font-size: 11px; font-weight: 700; margin-bottom: 12px; } +.partner-name { font-size: 16px; font-weight: 700; color: var(--gray-900); margin-bottom: 10px; } +.partner-desc { font-size: 13px; color: var(--gray-600); line-height: 1.6; } +.partner-cta { + margin-top: 64px; text-align: center; padding: 56px; + background: linear-gradient(135deg, var(--primary-light), rgba(0,163,224,.08)); + border-radius: 16px; border: 1px solid var(--gray-200); +} +.partner-cta h3 { font-size: 24px; font-weight: 800; margin-bottom: 12px; } +.partner-cta p { color: var(--gray-600); margin-bottom: 24px; font-size: 15px; } diff --git a/frontend/src/pages/Business.jsx b/frontend/src/pages/Business.jsx new file mode 100644 index 0000000..9170ffb --- /dev/null +++ b/frontend/src/pages/Business.jsx @@ -0,0 +1,211 @@ +import React, { useState } from 'react'; +import { Routes, Route, NavLink } from 'react-router-dom'; +import './Common.css'; +import './Business.css'; + +const SUB_NAV = [ + { path: '/business/reference', label: '๊ตฌ์ถ• ๋ ˆํผ๋Ÿฐ์Šค' }, + { path: '/business/partner', label: 'ํŒŒํŠธ๋„ˆ' }, +]; + +function SubNav({ title }) { + return ( + <> +
+
+ Business +

{title}

+
+
+ + + ); +} + +/* โ”€โ”€ ๋ ˆํผ๋Ÿฐ์Šค ๋ฐ์ดํ„ฐ โ”€โ”€ */ +const REFS = [ + { period:'24.12~25.02', client:'์— ๋กœ', project:'DELL ์ฐจ์„ธ๋Œ€ CRM ๊ตฌ์ถ•', role:'DBA', tech:'Oracle 19C, SQL/PLSQL, Java', category:'๊ธˆ์œตยท์ œ์กฐ' }, + { period:'24.09~25.10', client:'์‚ผ์„ฑ์ „์ž', project:'์‚ผ์„ฑ์ „์ž ์ฐจ์„ธ๋Œ€ CRM ๊ตฌ์ถ•', role:'DBํŠœ๋„ˆ',tech:'JBOSS, EDB, SQL/PLSQL, Java', category:'๋Œ€๊ธฐ์—…' }, + { period:'24.03~24.06', client:'์„œ์šธ์‹ ์šฉ๋ณด์ฆ์žฌ๋‹จ', project:'์†Œ์ƒ๊ณต์ธ ์ปจ์„คํŒ…์‹œ์Šคํ…œ ๊ตฌ์ถ•', role:'PM', tech:'JSP/Java, Websquare, Spring, Oracle', category:'๊ณต๊ณต๊ธฐ๊ด€' }, + { period:'23.11~24.02', client:'๊ตญ๋ฏผ์—ฐ๊ธˆ๊ด€๋ฆฌ๊ณต๋‹จ', project:'๊ตญ๋ฏผ์—ฐ๊ธˆ ์ฐจ์„ธ๋Œ€ ์‹œ์Šคํ…œ ๊ตฌ์ถ•', role:'AA', tech:'JSP/Java, Nexacro, Spring, CI/CD', category:'๊ณต๊ณต๊ธฐ๊ด€' }, + { period:'23.08~23.10', client:'ํ—Œ๋ฒ•์žฌํŒ์†Œ', project:'ํ—Œ๋ฒ•์žฌํŒ์†Œ ํฌํ„ธ์‹œ์Šคํ…œ ๊ตฌ์ถ•', role:'PM', tech:'Java, Egov, Spring, JEUS', category:'๊ณต๊ณต๊ธฐ๊ด€' }, + { period:'22.08~23.07', client:'์„œ์šธ์‹ ์šฉ๋ณด์ฆ์žฌ๋‹จ', project:'์žฌ๋‹จ ๋ชจ๋ฐ”์ผ์•ฑ ๊ตฌ์ถ•', role:'PM', tech:'Java, Nexacro, Spring, EDB', category:'๊ณต๊ณต๊ธฐ๊ด€' }, + { period:'22.01~22.07', client:'์—์ดํ…์—์ดํ”ผ', project:'ํ†ตํ•ฉ์œ ์ง€๋ณด์ˆ˜๊ด€๋ฆฌ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ', role:'PM', tech:'Java, Nexacro, Spring, Tomcat', category:'IT์„œ๋น„์Šค' }, + { period:'21.10~21.12', client:'ํ—Œ๋ฒ•์žฌํŒ์†Œ', project:'ํ†ตํ•ฉ๋ณด์•ˆ๊ด€์ œ์‹œ์Šคํ…œ ๊ตฌ์ถ• / DB์ด๊ด€', role:'PM', tech:'Java, Spring, JEUS, Oracle 12c', category:'๊ณต๊ณต๊ธฐ๊ด€' }, + { period:'20.05~21.09', client:'ํ˜„๋Œ€๋ฐฑํ™”์ ', project:'ํ˜„๋Œ€๋ฐฑํ™”์  HKOS ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ', role:'PM', tech:'Java, Nexacro, Spring, Pro*C', category:'์œ ํ†ตยท๋ฌผ๋ฅ˜' }, + { period:'20.12~21.04', client:'์„œ์šธ์‹œ๋ฆฝ๋Œ€', project:'๋Œ€ํ•™ํ–‰์ •์ •๋ณด์‹œ์Šคํ…œ ์„ฑ๋Šฅ๊ฐœ์„ ', role:'PL', tech:'Java, Spring, JMeter, JEUS, OZ', category:'๊ต์œก๊ธฐ๊ด€' }, + { period:'20.07~20.11', client:'์—์ดํ…์—์ดํ”ผ', project:'WMS ๊ณตํ†ต ํ”„๋ ˆ์ž„์›Œํฌ ๊ตฌ์ถ•', role:'PM', tech:'Java, Spring, Nexacro, Oracle', category:'IT์„œ๋น„์Šค' }, + { period:'20.01~20.06', client:'๋†ํ˜‘ํ•˜๋‚˜๋กœ๋งˆํŠธ', project:'๋†ํ˜‘ ํ•˜๋‚˜๋กœ๋งˆํŠธ ESL์‹œ์Šคํ…œ', role:'PM', tech:'C#, Java Spring Batch, REST API', category:'์œ ํ†ตยท๋ฌผ๋ฅ˜' }, + { period:'19.07~19.12', client:'์žฅ๋ณด๊ณ ์‹์ž์žฌ๋งˆํŠธ', project:'์ •์‚ฐ์‹œ์Šคํ…œ ๊ตฌ์ถ•', role:'PM', tech:'Java, Spring, Xplatform, Oracle', category:'์œ ํ†ตยท๋ฌผ๋ฅ˜' }, + { period:'19.01~19.06', client:'ํ•œํ™”๊ฐค๋Ÿฌ๋ฆฌ์•„', project:'๊ฐค๋Ÿฌ๋ฆฌ์•„๋ฐฑํ™”์  PDA ์ •์‚ฐ์‹œ์Šคํ…œ', role:'PM', tech:'Java, Spring, Xplatform, Oracle', category:'์œ ํ†ตยท๋ฌผ๋ฅ˜' }, + { period:'18.07~18.12', client:'์ด๋งˆํŠธ', project:'์ด๋งˆํŠธ ์ •์‚ฐ์‹œ์Šคํ…œ ํ”„๋กœ์ ํŠธ', role:'DA', tech:'Java, Spring, Xplatform, Oracle', category:'์œ ํ†ตยท๋ฌผ๋ฅ˜' }, + { period:'18.11~18.06', client:'์šฐ์ •์‚ฌ์—…์ •๋ณด์„ผํ„ฐ', project:'์šฐ์ฒด๊ตญ๊ธˆ์œต ์Šค๋งˆํŠธATM ๋„์ž…', role:'PMO', tech:'Visual C/C++, TCP/IP ์†Œ์ผ“ํ†ต์‹ ', category:'๊ณต๊ณต๊ธฐ๊ด€' }, + { period:'18.02~18.10', client:'ํ˜„๋Œ€๋ฐฑํ™”์ ', project:'๋ฌด์ธPOS์‹œ์Šคํ…œ ๊ตฌ์ถ•', role:'PM', tech:'Java, Spring, Xplatform, Oracle', category:'์œ ํ†ตยท๋ฌผ๋ฅ˜' }, + { period:'17.11~18.01', client:'KOCES', project:'KocesICPos ์ž๋™์—…๋ฐ์ดํŠธ ๋Ÿฐ์ฒ˜', role:'๊ฐœ๋ฐœ', tech:'C# .NET, Java/JSP, C/PRO*C', category:'๊ธˆ์œตยท์ œ์กฐ' }, + { period:'15.12~17.10', client:'LG U+', project:'LG U+ VAN ๊ณ ๋„ํ™”', role:'AA', tech:'Anylink/Tmax, WebLogic, C/PRO*C', category:'ํ†ต์‹ ยท๊ธˆ์œต' }, + { period:'15.07~15.11', client:'ํ•œํ™”S&C', project:'ํ•œํ™”๊ทธ๋ฃน 4์‚ฌ ํ†ตํ•ฉ HR์‹œ์Šคํ…œ', role:'PL', tech:'Java/JSP, Web Service (SOAP), IBSheet',category:'๋Œ€๊ธฐ์—…' }, + { period:'14.11~15.03', client:'์ฐธ์ข‹์€์—ฌํ–‰', project:'์ฝœ์„ผํ„ฐ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ•', role:'PL', tech:'ASP/.NET, Visual Studio 2012', category:'์„œ๋น„์Šค' }, + { period:'14.07~14.10', client:'ํ˜„๋Œ€์บํ”ผํƒˆ', project:'ํ˜„๋Œ€์บํ”ผํƒˆ ์ฐจ์„ธ๋Œ€์‹œ์Šคํ…œ', role:'PL', tech:'Java/JSP, Web Service, XPlatform', category:'๊ธˆ์œตยท์ œ์กฐ' }, + { period:'14.02~14.06', client:'์ค‘์†Œ๊ธฐ์—…์ฒญ', project:'์ค‘์†Œ๊ธฐ์—… 1357 ํ†ตํ•ฉ์ฝœ์„ผํ„ฐ', role:'PL', tech:'Java, Spring, XPlatform, ์ „์ž์ •๋ถ€FW', category:'๊ณต๊ณต๊ธฐ๊ด€' }, + { period:'13.08~13.12', client:'์‚ผ์„ฑ์ „์ž', project:'์‚ผ์„ฑ์ „์ž ํ’ˆ์งˆ๊ด€๋ฆฌ์‹œ์Šคํ…œ(QWINGS)', role:'PM', tech:'Java, Weblogic, Web Service, MiPlatform',category:'๋Œ€๊ธฐ์—…' }, + { period:'13.03~13.07', client:'๋Œ€์šฐ์ฆ๊ถŒ', project:'๋Œ€์šฐ์ฆ๊ถŒ ํ†ตํ•ฉ์ธํ”„๋ผ์‹œ์Šคํ…œ', role:'DBA', tech:'Java, Spring, XPlatform, Oracle', category:'ํ†ต์‹ ยท๊ธˆ์œต' }, + { period:'12.04~13.02', client:'์‚ผ์„ฑ์ „์ž์„œ๋น„์Šค', project:'eZone Renewal ํ”„๋กœ์ ํŠธ', role:'PL', tech:'Java, Weblogic, PRO*C, Android', category:'๋Œ€๊ธฐ์—…' }, + { period:'12.01~12.04', client:'๋†์ˆ˜์‚ฐ์‹ํ’ˆ์œ ํ†ต๊ณต์‚ฌ',project:'๋ฌด์—ญํ†ต๊ณ„์‹œ์Šคํ…œ ๊ตฌ์ถ•', role:'DBA', tech:'Java, Spring, Hibernate, ์ „์ž์ •๋ถ€FW', category:'๊ณต๊ณต๊ธฐ๊ด€' }, + { period:'11.02~11.12', client:'ํ˜„๋Œ€๋ชจ๋น„์Šค', project:'์›๊ฐ€๊ด€๋ฆฌ์‹œ์Šคํ…œ ๊ตฌ์ถ•', role:'DBA', tech:'Java, Spring, Hibernate, MiPlatform', category:'๋Œ€๊ธฐ์—…' }, + { period:'10.07~11.01', client:'ํ•œ๊ตญ์ „๊ธฐ์•ˆ์ „๊ณต์‚ฌ', project:'์ „๊ธฐ์•ˆ์ „ํฌํ„ธ์‹œ์Šคํ…œ ๊ตฌ์ถ•', role:'DBA', tech:'Java, Spring, Hibernate, XPlatform', category:'๊ณต๊ณต๊ธฐ๊ด€' }, + { period:'09.09~10.04', client:'์—ฝ์—ฐ์ดˆ์ƒ์‚ฐํ˜‘๋™์กฐํ•ฉ',project:'์—ฝ์—ฐ์ดˆ๊ฒฝ์ž‘ํ†ตํ•ฉ์‹œ์Šคํ…œ ๊ตฌ์ถ•', role:'PM', tech:'Java, Struts, i-Batis, Spring', category:'๊ณต๊ณต๊ธฐ๊ด€' }, + { period:'09.02~09.08', client:'ํ•œ๊ตญ์ „๊ธฐ์•ˆ์ „๊ณต์‚ฌ', project:'์•ˆ์ „์ ๊ฒ€ ๊ณ ๋„ํ™”', role:'DBA', tech:'Java, Weblogic, ์ „์ž์ •๋ถ€FW', category:'๊ณต๊ณต๊ธฐ๊ด€' }, + { period:'08.09~08.12', client:'๊ตญ๋ฏผ์€ํ–‰', project:'๊ตญ๋ฏผ์€ํ–‰ ์ฐจ์„ธ๋Œ€ ํฌํƒˆ', role:'PL', tech:'Java, AquaLogic, Struts/i-Batis', category:'ํ†ต์‹ ยท๊ธˆ์œต' }, + { period:'08.06~08.08', client:'ํ•œ๊ตญ์›์ž๋ ฅ์—ฐ๋ฃŒ', project:'์ธ์‚ฌ์ •๋ณด(HMS)์‹œ์Šคํ…œ', role:'DBA', tech:'Java/JSP/JSF, ์ž์ฒด ํ”„๋ ˆ์ž„์›Œํฌ', category:'๊ณต๊ณต๊ธฐ๊ด€' }, +]; + +const CATEGORIES = ['์ „์ฒด', '๊ณต๊ณต๊ธฐ๊ด€', '๋Œ€๊ธฐ์—…', '์œ ํ†ตยท๋ฌผ๋ฅ˜', 'ํ†ต์‹ ยท๊ธˆ์œต', '๊ธˆ์œตยท์ œ์กฐ', '๊ต์œก๊ธฐ๊ด€', 'IT์„œ๋น„์Šค', '์„œ๋น„์Šค']; + +const CAT_COLOR = { + '๊ณต๊ณต๊ธฐ๊ด€': '#0051A2', '๋Œ€๊ธฐ์—…': '#7c3aed', '์œ ํ†ตยท๋ฌผ๋ฅ˜': '#059669', + 'ํ†ต์‹ ยท๊ธˆ์œต': '#d97706', '๊ธˆ์œตยท์ œ์กฐ': '#dc2626', '๊ต์œก๊ธฐ๊ด€': '#0891b2', + 'IT์„œ๋น„์Šค': '#6366f1', '์„œ๋น„์Šค': '#db2777', +}; + +function Reference() { + const [cat, setCat] = useState('์ „์ฒด'); + const filtered = cat === '์ „์ฒด' ? REFS : REFS.filter(r => r.category === cat); + + return ( +
+ +
+
+
+ Reference +

๊ตฌ์ถ• ์‹ค์ 

+

2008๋…„๋ถ€ํ„ฐ ํ˜„์žฌ๊นŒ์ง€ ๊ตญ๋‚ด ์ฃผ์š” ๊ธฐ๊ด€ยท๊ธฐ์—… {REFS.length}๊ฐœ ํ”„๋กœ์ ํŠธ ์„ฑ๊ณต ์ˆ˜ํ–‰

+
+ + {/* ํ†ต๊ณ„ */} +
+ {[ + { val: `${REFS.length}+`, label: '์ด ํ”„๋กœ์ ํŠธ', color: 'var(--primary)' }, + { val: '20+', label: '๋…„ ๊ฒฝ๋ ฅ', color: 'var(--accent)' }, + { val: '15+', label: '๊ณต๊ณต๊ธฐ๊ด€', color: '#7c3aed' }, + { val: '10+', label: '๋Œ€๊ธฐ์—…ยท๊ธˆ์œต', color: '#059669' }, + ].map((s, i) => ( +
+
{s.val}
+

{s.label}

+
+ ))} +
+ + {/* ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ */} +
+ {CATEGORIES.map(c => ( + + ))} +
+ + {/* ํ…Œ์ด๋ธ” */} +
+ + + + + + + + + {filtered.map((r, i) => ( + + + + + + + + + ))} + +
๊ธฐ๊ฐ„๊ณ ๊ฐ์‚ฌํ”„๋กœ์ ํŠธ๋ช…์—ญํ• ์ฃผ์š”๊ธฐ์ˆ ๋ถ„์•ผ
{r.period}{r.client}{r.project}{r.role}{r.tech} + + {r.category} + +
+
+
+
+
+ ); +} + +/* โ”€โ”€ ํŒŒํŠธ๋„ˆ โ”€โ”€ */ +const PARTNERS = [ + { name: 'Oracle', logo: '๐Ÿ”ด', desc: 'Oracle DB ๊ณต์‹ ํŒŒํŠธ๋„ˆ โ€” Oracle 19c ์ „๋ฌธ DBA ์ธ์ฆ', tier: 'Gold' }, + { name: 'Red Hat', logo: '๐ŸŽฉ', desc: 'RHELยทOpenShift ํŒŒํŠธ๋„ˆ โ€” ๋ฆฌ๋ˆ…์Šค ์ธํ”„๋ผ ๊ตฌ์ถ•', tier: 'Silver' }, + { name: 'JEUS (TmaxSoft)', logo: 'โš™๏ธ', desc: '๊ตญ์‚ฐ WAS JEUS/Tmax ๊ณต์‹ ํŒŒํŠธ๋„ˆ', tier: 'Gold' }, + { name: 'Tibero', logo: '๐Ÿ—„๏ธ', desc: 'Tibero DBMS ๊ณต์‹ ํŒŒํŠธ๋„ˆ โ€” ๊ณต๊ณต๊ธฐ๊ด€ DB ์ „ํ™˜', tier: 'Gold' }, + { name: 'Samsung SDS', logo: '๐Ÿ’ผ', desc: '์‚ผ์„ฑSDS ํ˜‘๋ ฅ์‚ฌ โ€” ์‚ผ์„ฑ์ „์ž CRM/ํ’ˆ์งˆ ์‹œ์Šคํ…œ ๊ณต๋™ ์ˆ˜ํ–‰', tier: 'Partner' }, + { name: 'Nexacro', logo: '๐Ÿ–ฅ๏ธ', desc: 'ํˆฌ๋น„์†Œํ”„ํŠธ Nexacro ๊ณต์‹ ํŒŒํŠธ๋„ˆ โ€” UI ๊ฐœ๋ฐœ ์ „๋ฌธ', tier: 'Silver' }, + { name: 'OZ Report', logo: '๐Ÿ“Š', desc: 'OZ e-Form ๊ณต์‹ ํŒŒํŠธ๋„ˆ โ€” ๊ณต๊ณต ์ „์ž๋ฌธ์„œ ์†”๋ฃจ์…˜', tier: 'Silver' }, + { name: 'Ollama', logo: '๐Ÿค–', desc: '์˜จํ”„๋ ˆ๋ฏธ์Šค LLM ํŒŒํŠธ๋„ˆ โ€” GUARDiA AI ์—”์ง„ ๊ณต๊ธ‰์‚ฌ', tier: 'Tech' }, +]; + +const TIER_COLOR = { Gold:'#d97706', Silver:'#6b7280', Partner:'#0051A2', Tech:'#7c3aed' }; + +function Partner() { + return ( +
+ +
+
+
+ Partners +

๊ธฐ์ˆ  ํŒŒํŠธ๋„ˆ

+

์ตœ๊ณ ์˜ ๊ธฐ์ˆ  ํŒŒํŠธ๋„ˆ์™€ ํ•จ๊ป˜ ์ตœ์„ ์˜ ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค

+
+
+ {PARTNERS.map((p, i) => ( +
+
{p.logo}
+
+ {p.tier} Partner +
+

{p.name}

+

{p.desc}

+
+ ))} +
+ + {/* ํ˜‘๋ ฅ ์•ˆ๋‚ด */} +
+

ํŒŒํŠธ๋„ˆ์‹ญ ๋ฌธ์˜

+

์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ๊ณผ ๊ธฐ์ˆ  ํŒŒํŠธ๋„ˆ์‹ญ์„ ๋งบ๊ณ  ์‹ถ์œผ์‹  ๊ธฐ์—…์€ ์•„๋ž˜๋กœ ์—ฐ๋ฝ ์ฃผ์‹ญ์‹œ์˜ค.

+ + ํŒŒํŠธ๋„ˆ์‹ญ ์ œ์•ˆํ•˜๊ธฐ + +
+
+
+
+ ); +} + +export default function Business() { + return ( + + } /> + } /> + } /> + + ); +} diff --git a/frontend/src/pages/Company.css b/frontend/src/pages/Company.css new file mode 100644 index 0000000..b94e0f5 --- /dev/null +++ b/frontend/src/pages/Company.css @@ -0,0 +1,133 @@ +/* โ”€โ”€ ์„œ๋ธŒ ๋„ค๋น„ โ”€โ”€ */ +.sub-nav { background: var(--white); border-bottom: 1px solid var(--gray-200); } +.sub-nav .container { display: flex; gap: 0; overflow-x: auto; } +.sub-nav-item { + padding: 14px 22px; font-size: 14px; font-weight: 500; color: var(--gray-600); + white-space: nowrap; border-bottom: 2px solid transparent; + transition: all var(--fast) var(--ease); +} +.sub-nav-item:hover { color: var(--primary); } +.sub-nav-item.active { color: var(--primary); border-bottom-color: var(--primary); font-weight: 700; } + +/* โ”€โ”€ CEO โ”€โ”€ */ +.ceo-wrap { display: grid; grid-template-columns: 220px 1fr; gap: 60px; align-items: start; } +.ceo-photo { text-align: center; } +.ceo-avatar { + width: 160px; height: 160px; border-radius: 50%; + background: linear-gradient(135deg, var(--primary), var(--accent)); + display: flex; align-items: center; justify-content: center; + font-size: 28px; font-weight: 900; color: #fff; margin: 0 auto 16px; +} +.ceo-name { font-size: 18px; font-weight: 700; color: var(--gray-800); } +.ceo-sign { font-size: 13px; color: var(--gray-500); margin-top: 4px; } +.ceo-text h2 { font-size: 26px; font-weight: 900; color: var(--gray-900); line-height: 1.4; } +.ceo-para { font-size: 15px; line-height: 1.9; color: var(--gray-700); margin-bottom: 18px; } + +/* โ”€โ”€ ์—ฐํ˜ โ”€โ”€ */ +.timeline { position: relative; padding-left: 0; } +.timeline-row { + display: grid; grid-template-columns: 120px 24px 1fr; + gap: 0 24px; margin-bottom: 40px; align-items: start; +} +.timeline-year { + font-size: 22px; font-weight: 900; color: var(--primary); + text-align: right; padding-top: 2px; line-height: 1.2; +} +.timeline-dot { + width: 16px; height: 16px; border-radius: 50%; + background: var(--primary); border: 3px solid var(--primary-light); + margin-top: 4px; position: relative; flex-shrink: 0; +} +.timeline-dot::after { + content: ''; position: absolute; top: 100%; left: 50%; + transform: translateX(-50%); width: 2px; + height: calc(100% + 40px); background: var(--gray-200); +} +.timeline-row:last-child .timeline-dot::after { display: none; } +.timeline-content { padding-bottom: 8px; } +.timeline-item { + display: flex; gap: 10px; font-size: 15px; color: var(--gray-700); + margin-bottom: 8px; line-height: 1.6; align-items: flex-start; +} +.timeline-bullet { + width: 6px; height: 6px; border-radius: 50%; background: var(--accent); + flex-shrink: 0; margin-top: 7px; +} + +/* โ”€โ”€ ์กฐ์ง๋„ โ”€โ”€ */ +.org-chart { text-align: center; } +.org-top { display: flex; justify-content: center; margin-bottom: 0; } +.org-box { + padding: 14px 28px; border-radius: 10px; font-weight: 700; font-size: 15px; + display: inline-flex; align-items: center; justify-content: center; +} +.org-box.ceo { background: var(--secondary); color: #fff; min-width: 160px; font-size: 17px; } +.org-box.dept { + background: var(--white); border: 2px solid; min-width: 140px; + font-size: 14px; +} +.org-box.team { + background: var(--gray-50); border: 1px solid var(--gray-200); + color: var(--gray-700); font-size: 13px; padding: 10px 16px; + margin-bottom: 8px; min-width: 120px; font-weight: 500; +} +.org-line-v { width: 2px; height: 40px; background: var(--gray-300); margin: 0 auto; } +.org-line-v-short { width: 2px; height: 24px; background: var(--gray-300); margin: 0 auto; } +.org-depts { display: flex; justify-content: center; gap: 32px; position: relative; } +.org-dept-col { display: flex; flex-direction: column; align-items: center; } +.org-teams { display: flex; flex-direction: column; align-items: center; margin-top: 12px; } +.org-line-h { width: 100%; height: 2px; background: var(--gray-300); } + +/* โ”€โ”€ CI โ”€โ”€ */ +.ci-section { margin-bottom: 56px; } +.ci-title { font-size: 20px; font-weight: 800; color: var(--gray-900); margin-bottom: 24px; padding-bottom: 12px; border-bottom: 2px solid var(--gray-200); } +.ci-logo-wrap { display: flex; gap: 24px; flex-wrap: wrap; } +.ci-logo-box { + flex: 1; min-width: 200px; padding: 48px 32px; border-radius: 12px; + display: flex; flex-direction: column; align-items: center; gap: 16px; +} +.ci-logo-box.dark { background: var(--secondary); } +.ci-logo-box.light { background: var(--gray-50); border: 1px solid var(--gray-200); } +.ci-logo-text-wrap { display: flex; flex-direction: column; align-items: center; gap: 4px; } +.ci-logo-mark { font-size: 32px; font-weight: 900; color: var(--accent); letter-spacing: 4px; } +.ci-logo-sub { font-size: 14px; font-weight: 600; color: rgba(255,255,255,.7); letter-spacing: 2px; } +.ci-logo-label { font-size: 12px; color: rgba(255,255,255,.5); } +.ci-colors { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; } +.ci-color-card { border: 1px solid var(--gray-200); border-radius: 10px; overflow: hidden; } +.ci-color-swatch { height: 100px; } +.ci-color-info { padding: 16px; display: flex; flex-direction: column; gap: 4px; } +.ci-color-info strong { font-size: 14px; font-weight: 700; } +.ci-hex { font-size: 12px; font-family: monospace; color: var(--primary); } +.ci-cmyk { font-size: 11px; color: var(--gray-500); } +.ci-usage { font-size: 12px; color: var(--gray-600); } +.ci-slogan-wrap { background: linear-gradient(135deg, var(--secondary), var(--primary-dark)); border-radius: 16px; padding: 56px; text-align: center; } +.ci-slogan-main { font-size: 28px; font-weight: 900; color: #fff; margin-bottom: 12px; } +.ci-slogan-sub { font-size: 16px; color: rgba(255,255,255,.7); } + +/* โ”€โ”€ ์˜ค์‹œ๋Š” ๊ธธ โ”€โ”€ */ +.map-wrap { margin-bottom: 32px; } +.map-placeholder { + height: 320px; background: var(--gray-100); border-radius: 16px; + display: flex; flex-direction: column; align-items: center; justify-content: center; + border: 1px solid var(--gray-200); color: var(--gray-600); font-size: 16px; font-weight: 600; +} +.map-pin { font-size: 48px; margin-bottom: 12px; } +.location-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; } +.loc-office-title { font-size: 18px; font-weight: 700; margin-bottom: 20px; color: var(--gray-900); } +.loc-table { width: 100%; border-collapse: collapse; } +.loc-table th { width: 90px; text-align: left; font-size: 13px; color: var(--gray-500); font-weight: 600; padding: 10px 0; vertical-align: top; } +.loc-table td { font-size: 14px; color: var(--gray-700); padding: 10px 0; line-height: 1.6; border-bottom: 1px solid var(--gray-100); } +.transport-list { display: flex; flex-direction: column; gap: 20px; } +.transport-header { display: flex; gap: 8px; align-items: center; margin-bottom: 6px; } +.transport-header strong { font-size: 14px; font-weight: 700; color: var(--primary); } +.transport-item p { font-size: 13px; color: var(--gray-600); padding-left: 24px; line-height: 1.7; } + +/* โ”€โ”€ ๋ฐ˜์‘ํ˜• โ”€โ”€ */ +@media (max-width: 768px) { + .ceo-wrap { grid-template-columns: 1fr; } + .timeline-row { grid-template-columns: 80px 20px 1fr; gap: 0 16px; } + .timeline-year { font-size: 16px; } + .org-depts { flex-direction: column; align-items: center; gap: 24px; } + .ci-colors { grid-template-columns: repeat(2, 1fr); } + .location-grid { grid-template-columns: 1fr; } +} diff --git a/frontend/src/pages/Company.jsx b/frontend/src/pages/Company.jsx index ee6ba79..5e30cd5 100644 --- a/frontend/src/pages/Company.jsx +++ b/frontend/src/pages/Company.jsx @@ -1,38 +1,436 @@ -๏ปฟimport React from "react"; -import "./Common.css"; +import React from 'react'; +import { Routes, Route, NavLink, useNavigate } from 'react-router-dom'; +import './Common.css'; +import './Company.css'; -const SECTIONS = { - greeting: { - title: "CEO ์ธ์‚ฌ๋ง", - content: `์•ˆ๋…•ํ•˜์„ธ์š”. (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ๋Œ€ํ‘œ์ด์‚ฌ์ž…๋‹ˆ๋‹ค. +/* โ”€โ”€ ์„œ๋ธŒ ๋„ค๋น„ โ”€โ”€ */ +const SUB_NAV = [ + { path: '/company/greeting', label: 'CEO ์ธ์‚ฌ๋ง' }, + { path: '/company/history', label: '์—ฐํ˜' }, + { path: '/company/organization', label: '์กฐ์ง๋„' }, + { path: '/company/ci', label: 'CI ์†Œ๊ฐœ' }, + { path: '/company/location', label: '์˜ค์‹œ๋Š” ๊ธธ' }, +]; -์ €ํฌ ์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ์€ 2000๋…„ ์ฐฝ๋ฆฝ ์ด๋ž˜ 20์—ฌ ๋…„๊ฐ„ ๊ณต๊ณต๊ธฐ๊ด€ IT ์ „๋ฌธ ๊ธฐ์—…์œผ๋กœ ์„ฑ์žฅํ•ด ์™”์Šต๋‹ˆ๋‹ค. - -์ตœ๊ทผ GUARDiA ITSM ํ”Œ๋žซํผ์„ ํ†ตํ•ด AI ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ ์ž์œจ ์šด์˜์ด๋ผ๋Š” ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„์„ ์ œ์‹œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฉ”์‹ ์ € ํ•œ ์ค„ ๋ช…๋ น์œผ๋กœ 1,000๊ฐœ ์ด์ƒ์˜ ๊ด€๊ณต์„œ ๋ ˆ๊ฑฐ์‹œ ์ธํ”„๋ผ๋ฅผ ์ž๋™ํ™”ํ•˜๋Š” ํ˜์‹ ์ ์ธ ์†”๋ฃจ์…˜์œผ๋กœ, ๊ณต๊ณต๊ธฐ๊ด€์˜ ๋””์ง€ํ„ธ ์ „ํ™˜์„ ์ด๋Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. - -์•ž์œผ๋กœ๋„ ๊ณ ๊ฐ์˜ ์„ฑ๊ณต์ด ๊ณง ์ €ํฌ์˜ ์„ฑ๊ณต์ด๋ผ๋Š” ์‹ ๋… ์•„๋ž˜, ์ตœ๊ณ ์˜ ๊ธฐ์ˆ ๋ ฅ๊ณผ ์„œ๋น„์Šค๋กœ ๋ณด๋‹ตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. - -๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.` - }, -}; - -export default function Company() { +function SubNav({ title }) { return ( -
+ <>
Company -

ํšŒ์‚ฌ์†Œ๊ฐœ

+

{title}

-
-
-

CEO ์ธ์‚ฌ๋ง

- {SECTIONS.greeting.content.split("\n\n").map((p,i) => ( -

{p}

+ + + ); +} + +/* โ”€โ”€ CEO ์ธ์‚ฌ๋ง โ”€โ”€ */ +function Greeting() { + return ( +
+ +
+
+
+
+
+ CEO +
+

๋Œ€ํ‘œ์ด์‚ฌ

+

(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ 

+
+
+

์•ˆ๋…•ํ•˜์‹ญ๋‹ˆ๊นŒ,
(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ๋Œ€ํ‘œ์ด์‚ฌ์ž…๋‹ˆ๋‹ค.

+
+ {[ + '์ €ํฌ (์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ์€ 2000๋…„ ์ฐฝ๋ฆฝ ์ด๋ž˜ 20๋…„ ์ด์ƒ ๊ณต๊ณต๊ธฐ๊ด€ ๋ฐ ๋Œ€๊ธฐ์—… IT ์ „๋ฌธ ์„œ๋น„์Šค ๊ธฐ์—…์œผ๋กœ ์„ฑ์žฅํ•ด ์™”์Šต๋‹ˆ๋‹ค. ์‚ผ์„ฑ์ „์ž, ํ˜„๋Œ€๋ฐฑํ™”์ , ๊ตญ๋ฏผ์—ฐ๊ธˆ, ํ—Œ๋ฒ•์žฌํŒ์†Œ ๋“ฑ ๊ตญ๋‚ด ์ฃผ์š” ๊ธฐ๊ด€ยท๊ธฐ์—…์˜ ํ•ต์‹ฌ ์‹œ์Šคํ…œ์„ ์„ฑ๊ณต์ ์œผ๋กœ ๊ตฌ์ถ•ยท์šด์˜ํ•œ ํ’๋ถ€ํ•œ ๊ฒฝํ—˜์„ ๋ณด์œ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.', + '์ตœ๊ทผ์—๋Š” GUARDiA ITSM ํ”Œ๋žซํผ์„ ํ†ตํ•ด "AI ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ ์ž์œจ ์šด์˜"์ด๋ผ๋Š” ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„์„ ์ œ์‹œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฉ”์‹ ์ € ํ•œ ์ค„ ๋ช…๋ น์œผ๋กœ 1,000๊ฐœ ์ด์ƒ์˜ ๊ด€๊ณต์„œ ๋ ˆ๊ฑฐ์‹œ ์ธํ”„๋ผ๋ฅผ ์ž๋™ํ™”ํ•˜๋Š” ํ˜์‹ ์ ์ธ ์†”๋ฃจ์…˜์œผ๋กœ, ๋Œ€์ƒ ์„œ๋ฒ„์— ๋ณ„๋„ ์†Œํ”„ํŠธ์›จ์–ด ์„ค์น˜ ์—†์ด ํ‘œ์ค€ SSH/SFTP ํ”„๋กœํ† ์ฝœ๋งŒ์œผ๋กœ ์šด์˜ ์ž๋™ํ™”๋ฅผ ์‹คํ˜„ํ•ฉ๋‹ˆ๋‹ค.', + '์•ž์œผ๋กœ๋„ ๊ณ ๊ฐ์˜ ์„ฑ๊ณต์ด ๊ณง ์ €ํฌ์˜ ์„ฑ๊ณต์ด๋ผ๋Š” ์‹ ๋… ์•„๋ž˜, ์ตœ๊ณ ์˜ ๊ธฐ์ˆ ๋ ฅ๊ณผ ์„œ๋น„์Šค๋กœ ๋ณด๋‹ตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์–ธ์ œ๋‚˜ ์—ฌ๋Ÿฌ๋ถ„ ๊ณ์—์„œ ๋””์ง€ํ„ธ ํ˜์‹ ์˜ ํŒŒํŠธ๋„ˆ๊ฐ€ ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค.', + '๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.', + ].map((p, i) => ( +

{p}

+ ))} +
+
+ + {/* ํ•ต์‹ฌ ๊ฐ€์น˜ */} +
+
+ Core Values +

ํ•ต์‹ฌ ๊ฐ€์น˜

+
+
+ {[ + { icon: '๐ŸŽฏ', title: '๊ณ ๊ฐ ์ค‘์‹ฌ', desc: '๊ณ ๊ฐ์˜ ์„ฑ๊ณต์„ ์ตœ์šฐ์„ ์œผ๋กœ ์ƒ๊ฐํ•˜๋ฉฐ ์ตœ์ ์˜ ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค' }, + { icon: '๐Ÿš€', title: '๊ธฐ์ˆ  ํ˜์‹ ', desc: 'AIยทํด๋ผ์šฐ๋“œ ์ตœ์‹  ๊ธฐ์ˆ ๋กœ ๊ณ ๊ฐ์˜ ๋””์ง€ํ„ธ ์ „ํ™˜์„ ์„ ๋„ํ•ฉ๋‹ˆ๋‹ค' }, + { icon: '๐Ÿค', title: '์‹ ๋ขฐ์™€ ์ฑ…์ž„', desc: '20๋…„ ์ด์ƒ ์ถ•์ ๋œ ์‹ ๋ขฐ๋กœ ์ฑ…์ž„๊ฐ ์žˆ๋Š” ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค' }, + { icon: '๐ŸŒฑ', title: '์ง€์† ์„ฑ์žฅ', desc: '๊ตฌ์„ฑ์›๊ณผ ๊ณ ๊ฐ์ด ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ์ง€์† ๊ฐ€๋Šฅํ•œ ํŒŒํŠธ๋„ˆ์‹ญ์„ ์ถ”๊ตฌํ•ฉ๋‹ˆ๋‹ค' }, + ].map((v, i) => ( +
+
{v.icon}
+

{v.title}

+

{v.desc}

+
+ ))} +
+
+
); } + +/* โ”€โ”€ ์—ฐํ˜ โ”€โ”€ */ +const HISTORY = [ + { + year: '2026', items: [ + 'GUARDiA ITSM v2.0 ์ถœ์‹œ โ€” AI ChatOps ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ํ”Œ๋žซํผ', + 'GS์ธ์ฆ 1๋“ฑ๊ธ‰ ์‹ ์ฒญ ์ค€๋น„ ์™„๋ฃŒ (TTA ์‹ฌ์‚ฌ ์˜ˆ์ •)', + '๊ณต๊ณต๊ธฐ๊ด€ 1,000๊ฐœ ์ด์ƒ ๋ฉ€ํ‹ฐํ…Œ๋„ŒํŠธ ์ง€์› ๋ชฉํ‘œ ๋‹ฌ์„ฑ', + ] + }, + { + year: '2025', items: [ + '์‚ผ์„ฑ์ „์ž ์ฐจ์„ธ๋Œ€ CRM ๊ตฌ์ถ• (DB Migration / DA / ํŠœ๋‹)', + 'GUARDiA ITSM v1.0 ๋ฒ ํƒ€ ์„œ๋น„์Šค ๊ฐœ์‹œ', + 'AI ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ ์ž๋™ํ™” ํŠนํ—ˆ ์ถœ์›', + ] + }, + { + year: '2024', items: [ + 'DELL ์ฐจ์„ธ๋Œ€ CRM ๊ตฌ์ถ• โ€” DBA ์—ญํ•  ์ˆ˜ํ–‰ (์— ๋กœ)', + '์†Œ์ƒ๊ณต์ธ์ปจ์„คํŒ…์‹œ์Šคํ…œ ๊ตฌ์ถ• (์„œ์šธ์‹ ์šฉ๋ณด์ฆ์žฌ๋‹จ, PM)', + '๊ตญ๋ฏผ์—ฐ๊ธˆ ์ฐจ์„ธ๋Œ€ ์‹œ์Šคํ…œ ๊ตฌ์ถ• (AA)', + ] + }, + { + year: '2023', items: [ + 'ํ—Œ๋ฒ•์žฌํŒ์†Œ ํฌํ„ธ์‹œ์Šคํ…œ ๊ตฌ์ถ• (PM)', + '์„œ์šธ์‹ ์šฉ๋ณด์ฆ์žฌ๋‹จ ๋ชจ๋ฐ”์ผ์•ฑ ๊ตฌ์ถ• ์™„๋ฃŒ (PM)', + ] + }, + { + year: '2022', items: [ + '์—์ดํ…์—์ดํ”ผ ํ†ตํ•ฉ์œ ์ง€๋ณด์ˆ˜๊ด€๋ฆฌ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ (PM)', + 'ํ—Œ๋ฒ•์žฌํŒ์†Œ ํ†ตํ•ฉ๋ณด์•ˆ๊ด€์ œ์‹œ์Šคํ…œ ๊ตฌ์ถ• (PM)', + ] + }, + { + year: '2020โ€“2021', items: [ + 'ํ˜„๋Œ€๋ฐฑํ™”์  HKOS ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ/๊ตฌ์ถ• (PM)', + '์„œ์šธ์‹œ๋ฆฝ๋Œ€ ๋Œ€ํ•™ํ–‰์ •์ •๋ณด์‹œ์Šคํ…œ ์„ฑ๋Šฅ ๊ฐœ์„  (PL)', + '๋†ํ˜‘ ํ•˜๋‚˜๋กœ๋งˆํŠธ ESL ์‹œ์Šคํ…œ ๊ตฌ์ถ• (PM)', + ] + }, + { + year: '2018โ€“2019', items: [ + '์ด๋งˆํŠธ ์ •์‚ฐ์‹œ์Šคํ…œ ํ”„๋กœ์ ํŠธ (DA)', + '์šฐ์ฒด๊ตญ๊ธˆ์œต ์Šค๋งˆํŠธATM ๋„์ž… (PMO)', + 'ํ˜„๋Œ€๋ฐฑํ™”์  ๋ฌด์ธPOS์‹œ์Šคํ…œ ๊ตฌ์ถ• (PM)', + '๊ฐค๋Ÿฌ๋ฆฌ์•„๋ฐฑํ™”์  PDA ์ •์‚ฐ์‹œ์Šคํ…œ (PM)', + ] + }, + { + year: '2015โ€“2017', items: [ + 'LG U+ VAN ๊ณ ๋„ํ™” โ€” ์Šน์ธ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ FEP/AP/BEP (AA)', + 'ํ•œํ™”๊ทธ๋ฃน 4์‚ฌ ํ†ตํ•ฉ HR์‹œ์Šคํ…œ ๊ตฌ์ถ• (PL)', + '์ฐธ์ข‹์€์—ฌํ–‰ ์ฝœ์„ผํ„ฐ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ• (PL)', + ] + }, + { + year: '2013โ€“2014', items: [ + '์‚ผ์„ฑ์ „์ž ํ’ˆ์งˆ๊ด€๋ฆฌ์‹œ์Šคํ…œ(QWINGS) ๊ตฌ์ถ• (PM)', + '๋Œ€์šฐ์ฆ๊ถŒ ํ†ตํ•ฉ์ธํ”„๋ผ์‹œ์Šคํ…œ (DBA)', + 'ํ˜„๋Œ€์บํ”ผํƒˆ ์ฐจ์„ธ๋Œ€์‹œ์Šคํ…œ (PL)', + '์ค‘์†Œ๊ธฐ์—… 1357 ํ†ตํ•ฉ์ฝœ์„ผํ„ฐ ๊ตฌ์ถ• (PL)', + ] + }, + { + year: '2010โ€“2012', items: [ + '์‚ผ์„ฑ์ „์ž์„œ๋น„์Šค eZone ๊ฐฑ์‹  (PL)', + 'ํ˜„๋Œ€๋ชจ๋น„์Šค ์›๊ฐ€๊ด€๋ฆฌ์‹œ์Šคํ…œ (DBA)', + 'ํ•œ๊ตญ์ „๊ธฐ์•ˆ์ „๊ณต์‚ฌ ์ „๊ธฐ์•ˆ์ „ํฌํ„ธ์‹œ์Šคํ…œ (DBA)', + ] + }, + { + year: '2008โ€“2009', items: [ + '๊ตญ๋ฏผ์€ํ–‰ ์ฐจ์„ธ๋Œ€ ํฌํƒˆ ๊ตฌ์ถ• (PL)', + 'ํ•œ๊ตญ์›์ž๋ ฅ์—ฐ๋ฃŒ ์ธ์‚ฌ์ •๋ณด(HMS)์‹œ์Šคํ…œ (DBA)', + 'ํ•œ๊ตญ์ „๊ธฐ์•ˆ์ „๊ณต์‚ฌ ์•ˆ์ „์ ๊ฒ€ ๊ณ ๋„ํ™” (DBA)', + ] + }, + { + year: '2000', items: [ + '(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ  ์ฐฝ๋ฆฝ', + '๊ณต๊ณต๊ธฐ๊ด€ IT ์ธํ”„๋ผ ์„œ๋น„์Šค ๊ฐœ์‹œ', + ] + }, +]; + +function History() { + return ( +
+ +
+
+
+ History +

20๋…„+ ์„ฑ์žฅ์˜ ์—ญ์‚ฌ

+

2000๋…„ ์ฐฝ๋ฆฝ ์ด๋ž˜ ๊ตญ๋‚ด ์ฃผ์š” ๊ธฐ๊ด€ยท๊ธฐ์—…๊ณผ ํ•จ๊ป˜ ์„ฑ์žฅํ•ด ์™”์Šต๋‹ˆ๋‹ค

+
+
+ {HISTORY.map((h, i) => ( +
+
{h.year}
+
+
+ {h.items.map((item, j) => ( +
+ + {item} +
+ ))} +
+
+ ))} +
+
+
+
+ ); +} + +/* โ”€โ”€ ์กฐ์ง๋„ โ”€โ”€ */ +function Organization() { + return ( +
+ +
+
+
+ Organization +

์กฐ์ง ๊ตฌ์„ฑ

+
+
+ {/* ๋Œ€ํ‘œ์ด์‚ฌ */} +
+
๋Œ€ํ‘œ์ด์‚ฌ
+
+
+ {/* ๋ณธ๋ถ€ */} +
+ {[ + { + name: '๊ฐœ๋ฐœ๋ณธ๋ถ€', color: 'var(--primary)', + teams: ['AI๊ฐœ๋ฐœํŒ€', 'ํ”Œ๋žซํผ๊ฐœ๋ฐœํŒ€', 'ํ”„๋ก ํŠธ์—”๋“œํŒ€'] + }, + { + name: '๊ธฐ์ˆ ๋ณธ๋ถ€', color: 'var(--accent)', + teams: ['์ธํ”„๋ผํŒ€', 'DBAํŒ€', '๋ณด์•ˆํŒ€'] + }, + { + name: 'PM๋ณธ๋ถ€', color: '#7c3aed', + teams: ['SI์‚ฌ์—…ํŒ€', 'SM์šด์˜ํŒ€', 'PMOํŒ€'] + }, + { + name: '๊ฒฝ์˜์ง€์›๋ณธ๋ถ€', color: '#059669', + teams: ['๊ฒฝ์˜๊ธฐํšํŒ€', '์˜์—…ํŒ€', '์ธ์‚ฌยท์ด๋ฌดํŒ€'] + }, + ].map((dept, i) => ( +
+
+
+
+ {dept.name} +
+
+ {dept.teams.map((t, j) => ( +
{t}
+ ))} +
+
+ ))} +
+
+ + {/* ์ธ์› ํ˜„ํ™ฉ */} +
+ {[ + { label: '์ „์ฒด ์ž„์ง์›', value: '50+', unit: '๋ช…' }, + { label: '๊ฐœ๋ฐœ ์ธ๋ ฅ', value: '70', unit: '%' }, + { label: 'ํ‰๊ท  ๊ฒฝ๋ ฅ', value: '8', unit: '๋…„+' }, + { label: '๊ตญ๊ฐ€ ๊ณต์ธ ์ž๊ฒฉ', value: '30+', unit: '๊ฐœ' }, + ].map((s, i) => ( +
+
+ {s.value}{s.unit} +
+

{s.label}

+
+ ))} +
+
+
+
+ ); +} + +/* โ”€โ”€ CI ์†Œ๊ฐœ โ”€โ”€ */ +function CI() { + return ( +
+ +
+
+
+ Corporate Identity +

๋ธŒ๋žœ๋“œ ์•„์ด๋ดํ‹ฐํ‹ฐ

+

์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ์˜ ๋ธŒ๋žœ๋“œ๋Š” ์‹ ๋ขฐยทํ˜์‹ ยท์ „๋ฌธ์„ฑ์„ ์ƒ์ง•ํ•ฉ๋‹ˆ๋‹ค

+
+ + {/* ๋กœ๊ณ  */} +
+

๋กœ๊ณ  ์‹œ์Šคํ…œ

+
+
+
+ GEO + ์ •๋ณด๊ธฐ์ˆ  +
+

๋‹คํฌ ๋ฐฐ๊ฒฝ์šฉ

+
+
+
+ GEO + ์ •๋ณด๊ธฐ์ˆ  +
+

๋ผ์ดํŠธ ๋ฐฐ๊ฒฝ์šฉ

+
+
+
+ + {/* ๋ธŒ๋žœ๋“œ ์ปฌ๋Ÿฌ */} +
+

๋ธŒ๋žœ๋“œ ์ปฌ๋Ÿฌ

+
+ {[ + { name: 'Primary Blue', hex: '#0051A2', cmyk: 'C100 M50 Y0 K37', usage: '์ฃผ์š” UIยท๋ฒ„ํŠผยท๊ฐ•์กฐ' }, + { name: 'Accent Blue', hex: '#00A3E0', cmyk: 'C100 M28 Y0 K12', usage: 'ํฌ์ธํŠธยท๋งํฌยท์•„์ด์ฝ˜' }, + { name: 'Dark Navy', hex: '#1A1A2E', cmyk: 'C40 M40 Y0 K82', usage: 'ํ—ค๋”ยท๋ฐฐ๊ฒฝยทํ…์ŠคํŠธ' }, + { name: 'Pure White', hex: '#FFFFFF', cmyk: 'C0 M0 Y0 K0', usage: '๋ฐฐ๊ฒฝยท๋ฐ˜์ „ ํ…์ŠคํŠธ' }, + ].map((c, i) => ( +
+
+
+ {c.name} + {c.hex} + {c.cmyk} + {c.usage} +
+
+ ))} +
+
+ + {/* ์Šฌ๋กœ๊ฑด */} +
+

๋ธŒ๋žœ๋“œ ์Šฌ๋กœ๊ฑด

+
+

"๊ณต๊ณต๊ธฐ๊ด€ IT, ์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ๊ณผ ํ•จ๊ป˜"

+

20๋…„ ์‹ ๋ขฐ์˜ ๊ธฐ์ˆ ๋ ฅ์œผ๋กœ AI ์ธํ”„๋ผ ํ˜์‹ ์„ ์ด๋•๋‹ˆ๋‹ค

+
+
+
+
+
+ ); +} + +/* โ”€โ”€ ์˜ค์‹œ๋Š” ๊ธธ โ”€โ”€ */ +function Location() { + return ( +
+ +
+
+
+ Location +

์˜ค์‹œ๋Š” ๊ธธ

+
+ + {/* ์ง€๋„ ์˜์—ญ */} +
+
+
๐Ÿ“
+

(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ 

+

+ ์„œ์šธํŠน๋ณ„์‹œ ๊ฐ•์„œ๊ตฌ ์–‘์ฒœ๋กœ 570 +

+
+
+ +
+ {/* ๋ณธ์‚ฌ */} +
+

๐Ÿ“ ๋ณธ์‚ฌ

+ + + + + + + + +
์ฃผ์†Œ์„œ์šธํŠน๋ณ„์‹œ ๊ฐ•์„œ๊ตฌ ์–‘์ฒœ๋กœ 570
NH์„œ์šธํƒ€์›Œ 5์ธต
๋Œ€ํ‘œ์ „ํ™”02-784-9271
ํŒฉ์Šค02-784-9272
์ด๋ฉ”์ผinfo@zioinfo.co.kr
์šด์˜์‹œ๊ฐ„ํ‰์ผ 09:00 ~ 18:00 (์ ์‹ฌ 12:00~13:00)
+
+ + {/* ๊ตํ†ต ์•ˆ๋‚ด */} +
+

๐Ÿš‡ ๊ตํ†ต ์•ˆ๋‚ด

+
+ {[ + { type: '์ง€ํ•˜์ฒ ', icon: '๐Ÿš‡', lines: ['5ํ˜ธ์„  ๋ฐœ์‚ฐ์—ญ 1๋ฒˆ ์ถœ๊ตฌ ๋„๋ณด 5๋ถ„', '9ํ˜ธ์„  ๋งˆ๊ณก๋‚˜๋ฃจ์—ญ 3๋ฒˆ ์ถœ๊ตฌ ๋„๋ณด 10๋ถ„'] }, + { type: '๋ฒ„์Šค', icon: '๐ŸšŒ', lines: ['๊ฐ•์„œ05, ๊ฐ•์„œ06 โ€” ๊ฐ•์„œ๊ตฌ์ฒญ ์ •๋ฅ˜์žฅ ํ•˜์ฐจ', '60, 62, 604 โ€” ๋ฐœ์‚ฐ์—ญ ํ•˜์ฐจ'] }, + { type: '์ž๊ฐ€์šฉ', icon: '๐Ÿš—', lines: ['์˜ฌ๋ฆผํ”ฝ๋Œ€๋กœ โ†’ ๊ฐ•์„œIC โ†’ ์–‘์ฒœ๋กœ ๋ฐฉ๋ฉด', '์ฃผ์ฐจ ๊ฐ€๋Šฅ (๋ฐฉ๋ฌธ ์ „ ์‚ฌ์ „ ์—ฐ๋ฝ ๊ถŒ์žฅ)'] }, + ].map((t, i) => ( +
+
+ {t.icon} + {t.type} +
+ {t.lines.map((l, j) =>

{l}

)} +
+ ))} +
+
+
+
+
+
+ ); +} + +/* โ”€โ”€ ๋ผ์šฐํ„ฐ โ”€โ”€ */ +export default function Company() { + return ( + + } /> + } /> + } /> + } /> + } /> + } /> + + ); +} diff --git a/frontend/src/pages/NewsPage.css b/frontend/src/pages/NewsPage.css new file mode 100644 index 0000000..a30e39e --- /dev/null +++ b/frontend/src/pages/NewsPage.css @@ -0,0 +1,19 @@ +.notice-back { font-size:14px; color:var(--primary); margin-bottom:24px; display:inline-flex; align-items:center; gap:4px; cursor:pointer; background:none; border:none; } +.news-cat-badge { display:inline-block; padding:4px 12px; border-radius:12px; font-size:12px; font-weight:700; margin-bottom:12px; } +.news-cat-badge.hot { background:rgba(239,68,68,.12); color:var(--danger); } +.news-main { padding:40px; cursor:pointer; background:linear-gradient(135deg, var(--secondary), var(--primary-dark)); border:none; margin-bottom:0; } +.news-main:hover { transform:none; box-shadow:var(--shadow-lg); } +.news-main-title { font-size:26px; font-weight:900; color:#fff; margin-bottom:16px; line-height:1.35; } +.news-main-summary { font-size:15px; color:rgba(255,255,255,.7); line-height:1.8; margin-bottom:16px; max-width:640px; } +.news-date { font-size:12px; color:rgba(255,255,255,.5); } +.news-card { padding:28px; cursor:pointer; display:flex; flex-direction:column; } +.news-card-title { font-size:15px; font-weight:700; color:var(--gray-900); margin-bottom:10px; line-height:1.5; flex:1; } +.news-card-summary { font-size:13px; color:var(--gray-600); line-height:1.7; margin-bottom:12px; flex:1; } +.news-date { font-size:12px; color:var(--gray-400); } +.blog-card { padding:28px; display:flex; flex-direction:column; } +.blog-tag { display:inline-block; padding:4px 12px; border-radius:12px; font-size:12px; font-weight:700; margin-bottom:14px; align-self:flex-start; } +.blog-title { font-size:16px; font-weight:700; color:var(--gray-900); line-height:1.5; margin-bottom:12px; } +.blog-summary { font-size:13px; color:var(--gray-600); line-height:1.7; flex:1; margin-bottom:16px; } +.blog-meta { display:flex; gap:16px; font-size:12px; color:var(--gray-400); margin-bottom:16px; } +.blog-read-btn { padding:10px 20px; background:var(--primary-light); color:var(--primary); border-radius:8px; font-size:14px; font-weight:700; border:none; cursor:pointer; transition:all var(--fast); text-align:center; } +.blog-read-btn:hover { background:var(--primary); color:#fff; } diff --git a/frontend/src/pages/NewsPage.jsx b/frontend/src/pages/NewsPage.jsx index de6b620..a9df16f 100644 --- a/frontend/src/pages/NewsPage.jsx +++ b/frontend/src/pages/NewsPage.jsx @@ -1,30 +1,203 @@ -๏ปฟimport React, { useState, useEffect } from "react"; -import { Link } from "react-router-dom"; -import axios from "axios"; -import "./Common.css"; +import React, { useState } from 'react'; +import { Routes, Route, NavLink, Link } from 'react-router-dom'; +import './Common.css'; +import './NewsPage.css'; -export default function NewsPage() { - const [news, setNews] = useState([]); - useEffect(() => { axios.get("/api/news?size=12").then(r => setNews(r.data.content || [])); }, []); +const SUB_NAV = [ + { path: '/news/newsroom', label: '๋‰ด์Šค๋ฃธ' }, + { path: '/news/blog', label: '๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ' }, +]; + +function SubNav({ title }) { return ( -
+ <>
News -

๋‰ด์Šค๋ฃธ

+

{title}

+ + + ); +} + +/* โ”€โ”€ ๋‰ด์Šค๋ฃธ โ”€โ”€ */ +const NEWS = [ + { + id:1, cat:'์ œํ’ˆ ์ถœ์‹œ', date:'2026.05.15', + title:'GUARDiA ITSM v2.0 ์ •์‹ ์ถœ์‹œ โ€” AI ChatOps ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ํ”Œ๋žซํผ', + summary:'๋ฉ”์‹ ์ € ํ•œ ์ค„ ๋ช…๋ น์œผ๋กœ 1,000๊ฐœ+ ๊ณต๊ณต๊ธฐ๊ด€ ๋ ˆ๊ฑฐ์‹œ ์ธํ”„๋ผ๋ฅผ ์ž๋™ ์šด์˜ํ•˜๋Š” GUARDiA ITSM v2.0์ด ์ •์‹ ์ถœ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์‹ ๊ทœ ๊ธฐ๋Šฅ์œผ๋กœ AI ์ž์—ฐ์–ด ๋ช…๋ น, ์—์ด์ „ํŠธ๋ฆฌ์Šค ๋ฐฐํฌ ์—”์ง„, ๋ฉ€ํ‹ฐํ…Œ๋„ŒํŠธ ์ง€์›์ด ์ถ”๊ฐ€๋์Šต๋‹ˆ๋‹ค.', + content: `GUARDiA ITSM v2.0์€ ๊ณต๊ณต๊ธฐ๊ด€์˜ ๋ ˆ๊ฑฐ์‹œ IT ์ธํ”„๋ผ ์šด์˜ ์ž๋™ํ™”๋ฅผ ์œ„ํ•œ AI ๊ธฐ๋ฐ˜ ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค.\n\n์ฃผ์š” ์‹ ๊ธฐ๋Šฅ:\n- AI ChatOps: ๋ฉ”์‹ ์ € ์ž์—ฐ์–ด ๋ช…๋ น โ†’ Ollama LLM ํŒŒ์‹ฑ โ†’ ์ž๋™ ์‹คํ–‰\n- ์—์ด์ „ํŠธ๋ฆฌ์Šค ๋ฐฐํฌ: SSH/SFTP๋งŒ์œผ๋กœ WAS ๋ฐฐํฌยท๋กค๋ฐฑ ์ž๋™ํ™”\n- ๋ฉ€ํ‹ฐํ…Œ๋„ŒํŠธ: 1,000๊ฐœ+ ๊ธฐ๊ด€ ๋™์‹œ ๊ด€๋ฆฌ\n- GS์ธ์ฆ 1๋“ฑ๊ธ‰ ์‹ ์ฒญ ์™„๋ฃŒ\n\n์ž์„ธํ•œ ์‚ฌํ•ญ์€ GUARDiA ์†Œ๊ฐœ ํŽ˜์ด์ง€๋ฅผ ์ฐธ์กฐํ•ด ์ฃผ์‹ญ์‹œ์˜ค.`, + hot: true, + }, + { + id:2, cat:'์ˆ˜์ฃผ ์†Œ์‹', date:'2026.04.20', + title:'์‚ผ์„ฑ์ „์ž ์ฐจ์„ธ๋Œ€ CRM ์‹œ์Šคํ…œ DB ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ”„๋กœ์ ํŠธ ์ˆ˜์ฃผ', + summary:'(์ฃผ)์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ์ด ์‚ผ์„ฑ์ „์ž ์ฐจ์„ธ๋Œ€ CRM ๊ตฌ์ถ• ํ”„๋กœ์ ํŠธ์˜ DB Migration/DA/ํŠœ๋‹์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค. EDB PostgreSQL ํ™˜๊ฒฝ์œผ๋กœ์˜ ์ „ํ™˜์„ ํฌํ•จํ•œ ๋Œ€๊ทœ๋ชจ DB ํ˜„๋Œ€ํ™” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.', + content: '์‚ผ์„ฑ์ „์ž์™€์˜ ๋‘ ๋ฒˆ์งธ ํ˜‘๋ ฅ ํ”„๋กœ์ ํŠธ๋กœ, DB ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐ ์„ฑ๋Šฅ ํŠœ๋‹์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.', + hot: false, + }, + { + id:3, cat:'๊ธฐ์ˆ  ์ธ์ฆ', date:'2026.03.10', + title:'GUARDiA ITSM GS์ธ์ฆ 1๋“ฑ๊ธ‰ ์‹ ์ฒญ ์™„๋ฃŒ โ€” TTA ์‹ฌ์‚ฌ ์˜ˆ์ •', + summary:'GUARDiA ITSM์ด ํ•œ๊ตญ์ •๋ณดํ†ต์‹ ๊ธฐ์ˆ ํ˜‘ํšŒ(TTA)์— GS์ธ์ฆ 1๋“ฑ๊ธ‰์„ ์‹ ์ฒญํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ์ ํ•ฉ์„ฑ, ์‹ ๋ขฐ์„ฑ, ์‚ฌ์šฉ์„ฑ, ๋ณด์•ˆ์„ฑ ๋“ฑ ISO/IEC 25010 ๊ธฐ์ค€ 8๋Œ€ ํ’ˆ์งˆ ํŠน์„ฑ ์‹ฌ์‚ฌ๋ฅผ ์•ž๋‘๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.', + content: 'GS์ธ์ฆ ์‹ฌ์‚ฌ๋Š” 2026๋…„ 9์›” ์˜ˆ์ •์ด๋ฉฐ, 1๋“ฑ๊ธ‰ ์ทจ๋“ ์‹œ ์กฐ๋‹ฌ์ฒญ ๋‚˜๋ผ์žฅํ„ฐ ์šฐ์„  ๋“ฑ์žฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.', + hot: false, + }, + { + id:4, cat:'์ˆ˜์ฃผ ์†Œ์‹', date:'2026.02.15', + title:'๊ตญ๋ฏผ์—ฐ๊ธˆ๊ณต๋‹จ ์ฐจ์„ธ๋Œ€ ์‹œ์Šคํ…œ ๊ตฌ์ถ• โ€” AA ์—ญํ•  ์ˆ˜ํ–‰', + summary:'๊ตญ๋ฏผ์—ฐ๊ธˆ๊ณต๋‹จ ์ฐจ์„ธ๋Œ€ ์‹œ์Šคํ…œ ๊ตฌ์ถ• ํ”„๋กœ์ ํŠธ์— Application Architect(AA)๋กœ ์ฐธ์—ฌํ•ฉ๋‹ˆ๋‹ค. JSP/Java, Nexacro, Spring ๊ธฐ๋ฐ˜์˜ ๋Œ€๊ทœ๋ชจ ๊ณต๊ณต๊ธฐ๊ด€ ์‹œ์Šคํ…œ ๊ตฌ์ถ•์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.', + content: '๊ตญ๋ฏผ์—ฐ๊ธˆ๊ด€๋ฆฌ๊ณต๋‹จ์˜ ์ฐจ์„ธ๋Œ€ ์‹œ์Šคํ…œ์€ ์ˆ˜์ฒœ๋งŒ ๊ฐ€์ž…์ž์˜ ์—ฐ๊ธˆ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์œผ๋กœ, CI/CD ํŒŒ์ดํ”„๋ผ์ธ ๊ธฐ๋ฐ˜์˜ ํ˜„๋Œ€์ ์ธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค.', + hot: false, + }, + { + id:5, cat:'๊ธฐ์—… ์†Œ์‹', date:'2025.12.01', + title:'2025๋…„ ์‚ฌ์—…์‹ค์  โ€” ์—ฐ๊ฐ„ ํ”„๋กœ์ ํŠธ 10๊ฑด ์„ฑ๊ณต ์ˆ˜ํ–‰', + summary:'2025๋…„ ํ•œ ํ•ด ๋™์•ˆ ์‚ผ์„ฑ์ „์ž, ์„œ์šธ์‹ ์šฉ๋ณด์ฆ์žฌ๋‹จ, ํ—Œ๋ฒ•์žฌํŒ์†Œ ๋“ฑ 10๊ฐœ ์ฃผ์š” ํ”„๋กœ์ ํŠธ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋งค์ถœ์€ ์ „๋…„ ๋Œ€๋น„ 25% ์„ฑ์žฅํ•˜์˜€์Šต๋‹ˆ๋‹ค.', + content: '์ฐฝ๋ฆฝ ์ด๋ž˜ ์ตœ๋Œ€ ์„ฑ๊ณผ๋ฅผ ๊ธฐ๋กํ•œ 2025๋…„ ์‚ฌ์—…์‹ค์ ์„ ๊ณต์œ ๋“œ๋ฆฝ๋‹ˆ๋‹ค.', + hot: false, + }, + { + id:6, cat:'ํŒŒํŠธ๋„ˆ์‹ญ', date:'2025.09.10', + title:'Tibero ๊ณต์‹ ํŒŒํŠธ๋„ˆ์‚ฌ ๋“ฑ๋ก โ€” ๊ณต๊ณต๊ธฐ๊ด€ DB ์ „ํ™˜ ์†”๋ฃจ์…˜ ๊ฐ•ํ™”', + summary:'๊ตญ์‚ฐ DBMS Tibero์˜ ๊ณต์‹ ํŒŒํŠธ๋„ˆ์‚ฌ๋กœ ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Oracle์—์„œ Tibero๋กœ์˜ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐ ๊ณต๊ณต๊ธฐ๊ด€ DB ํ˜„๋Œ€ํ™” ์‚ฌ์—…์„ ๊ณต๋™์œผ๋กœ ์ถ”์ง„ํ•ฉ๋‹ˆ๋‹ค.', + content: '๊ณต๊ณต๊ธฐ๊ด€์˜ Oracle ๋ผ์ด์„ ์Šค ์ ˆ๊ฐ์„ ์œ„ํ•œ Tibero ์ „ํ™˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์ „๋ฌธ์ ์œผ๋กœ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.', + hot: false, + }, +]; + +function Newsroom() { + const [selected, setSelected] = useState(null); + const item = NEWS.find(n => n.id === selected); + return ( +
+
+ {item ? ( +
+ +
+ {item.cat} +

{item.title}

+

{item.date}

+
+ {item.content.split('\n').map((p, i) => ( + p.trim() ?

{p}

: null + ))} +
+
+ ) : ( + <> + {/* ๋ฉ”์ธ ๋‰ด์Šค */} +
setSelected(NEWS[0].id)}> +
+ ๐Ÿ”ฅ {NEWS[0].cat} +

{NEWS[0].title}

+

{NEWS[0].summary}

+ {NEWS[0].date} +
+
+
+ {NEWS.slice(1).map(n => ( +
setSelected(n.id)}> + {n.cat} +

{n.title}

+

{n.summary}

+ {n.date} +
+ ))} +
+ + )} +
+
+
+ ); +} + +/* โ”€โ”€ ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ โ”€โ”€ */ +const BLOGS = [ + { + id:1, tag:'AIยทLLM', date:'2026.05.20', + title:'์˜จํ”„๋ ˆ๋ฏธ์Šค Ollama๋กœ ํ์‡„๋ง ChatOps ๊ตฌํ˜„ํ•˜๊ธฐ', + summary:'์ธํ„ฐ๋„ท ์—†์ด ๋‚ด๋ถ€๋ง์—์„œ LLM์„ ์šด์˜ํ•˜๋Š” ๋ฐฉ๋ฒ•. Llama-3-8B ๋ชจ๋ธ์„ Ollama๋กœ ๊ตฌ๋™ํ•˜๊ณ  FastAPI์™€ ์—ฐ๋™ํ•˜๋Š” ์ „์ฒด ๊ณผ์ •์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.', + readMin: 12, + }, + { + id:2, tag:'DevOps', date:'2026.05.10', + title:'์—์ด์ „ํŠธ๋ฆฌ์Šค WAS ๋ฐฐํฌ ์ž๋™ํ™” โ€” paramiko SSH๋กœ ๋ ˆ๊ฑฐ์‹œ ์„œ๋ฒ„ ๊ด€๋ฆฌ', + summary:'JEUSยทTomcat ๋“ฑ ๋ ˆ๊ฑฐ์‹œ WAS์— SSH/SFTP๋งŒ์œผ๋กœ ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ๋ฒ•. ๋ฐฑ์—…โ†’๋ฐฐํฌโ†’ํ—ฌ์Šค์ฒดํฌโ†’๋กค๋ฐฑ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌํ˜„ ์˜ˆ์ œ.', + readMin: 15, + }, + { + id:3, tag:'๋ณด์•ˆ', date:'2026.04.28', + title:'AES-256-GCM์œผ๋กœ ์„œ๋ฒ„ ์ž๊ฒฉ์ฆ๋ช…์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ•˜๋Š” ๋ฒ•', + summary:'๊ณต๊ณต๊ธฐ๊ด€ ์„œ๋ฒ„ SSH ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ DB์— ์•ˆ์ „ํ•˜๊ฒŒ ์•”ํ˜ธํ™” ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•. IVยท์•”ํ˜ธ๋ฌธยทGCM Tag ๊ตฌ์กฐ ์„ค๊ณ„์™€ Python ๊ตฌํ˜„.', + readMin: 8, + }, + { + id:4, tag:'๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค', date:'2026.04.15', + title:'Oracle 19c โ†’ EDB PostgreSQL ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹ค์ „ ๊ฐ€์ด๋“œ', + summary:'์‚ผ์„ฑ์ „์ž CRM ํ”„๋กœ์ ํŠธ์—์„œ ์‹ค์ œ ์ˆ˜ํ–‰ํ•œ Oracleโ†’EDB ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฒฝํ—˜ ๊ณต์œ . Smeta, ExemOne ํ™œ์šฉ SQL ๋ณ€ํ™˜ ์ „๋žต.', + readMin: 20, + }, + { + id:5, tag:'์„ฑ๋Šฅ', date:'2026.03.25', + title:'๊ณต๊ณต๊ธฐ๊ด€ ํ–‰์ •์ •๋ณด์‹œ์Šคํ…œ SQL ํŠœ๋‹ โ€” ์„œ์šธ์‹œ๋ฆฝ๋Œ€ ์‚ฌ๋ก€', + summary:'๋Œ€ํ•™ํ–‰์ •์ •๋ณด์‹œ์Šคํ…œ ์„ฑ๋Šฅ ๊ฐœ์„  ํ”„๋กœ์ ํŠธ ์‹ค์ „ ์‚ฌ๋ก€. JMeter ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ์™€ Oracle ์‹คํ–‰๊ณ„ํš ๋ถ„์„์œผ๋กœ ์‘๋‹ต์‹œ๊ฐ„ 60% ๋‹จ์ถ•.', + readMin: 18, + }, + { + id:6, tag:'์•„ํ‚คํ…์ฒ˜', date:'2026.03.10', + title:'FastAPI ๋น„๋™๊ธฐ WebSocket์œผ๋กœ ์‹ค์‹œ๊ฐ„ ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์ถ•ํ•˜๊ธฐ', + summary:'GUARDiA ITSM ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง ๋Œ€์‹œ๋ณด๋“œ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•. FastAPI SSE + WebSocket + React๋ฅผ ์กฐํ•ฉํ•œ ํ’€์Šคํƒ ์•„ํ‚คํ…์ฒ˜.', + readMin: 14, + }, +]; + +const TAG_COLORS = { + 'AIยทLLM': '#7c3aed', 'DevOps': '#0051A2', '๋ณด์•ˆ': '#dc2626', + '๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค': '#d97706', '์„ฑ๋Šฅ': '#059669', '์•„ํ‚คํ…์ฒ˜': '#0891b2' +}; + +function Blog() { + return ( +
+ +
+
+
+ Tech Blog +

๊ธฐ์ˆ  ์ธ์‚ฌ์ดํŠธ ๊ณต์œ 

+

20๋…„ ์ด์ƒ์˜ ํ”„๋กœ์ ํŠธ ๊ฒฝํ—˜์—์„œ ์–ป์€ ๊ธฐ์ˆ  ๋…ธํ•˜์šฐ๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค

+
- {news.map(n => ( -
- {n.category} -

{n.title}

-

{n.summary}

- - {n.createdAt ? new Date(n.createdAt).toLocaleDateString("ko-KR") : ""} - + {BLOGS.map(b => ( +
+
+ {b.tag} +
+

{b.title}

+

{b.summary}

+
+ ๐Ÿ“… {b.date} + โฑ {b.readMin}๋ถ„ ์ฝ๊ธฐ +
+
))}
@@ -33,3 +206,14 @@ export default function NewsPage() {
); } + +export default function NewsPage() { + return ( + + } /> + } /> + } /> + } /> + + ); +} diff --git a/frontend/src/pages/Recruit.css b/frontend/src/pages/Recruit.css new file mode 100644 index 0000000..375f9de --- /dev/null +++ b/frontend/src/pages/Recruit.css @@ -0,0 +1,34 @@ +.notice-back { font-size:14px; color:var(--primary); margin-bottom:24px; display:inline-flex; align-items:center; gap:4px; cursor:pointer; background:none; border:none; } +.job-card { padding:28px; display:flex; gap:24px; align-items:flex-start; cursor:pointer; } +.job-card:hover { border-color:var(--primary); } +.job-info { flex:1; } +.job-title { font-size:18px; font-weight:700; color:var(--gray-900); margin-bottom:8px; } +.job-desc { font-size:13px; color:var(--gray-600); line-height:1.6; margin-bottom:12px; } +.job-stack { display:flex; gap:6px; flex-wrap:wrap; } +.job-tech { padding:4px 10px; background:var(--secondary); color:var(--accent); border-radius:4px; font-size:11px; font-weight:600; } +.job-meta { display:flex; flex-direction:column; gap:8px; align-items:flex-end; min-width:100px; } +.job-meta > div { display:flex; flex-direction:column; align-items:flex-end; font-size:13px; color:var(--gray-700); } +.job-meta-label { font-size:11px; color:var(--gray-400); margin-bottom:2px; } +.welfare-cat { font-size:18px; font-weight:800; color:var(--gray-900); margin-bottom:20px; } +.welfare-card { padding:28px; text-align:center; } +.welfare-icon { font-size:36px; margin-bottom:12px; } +.welfare-name { font-size:15px; font-weight:700; margin-bottom:8px; color:var(--gray-900); } +.welfare-desc { font-size:13px; color:var(--gray-600); line-height:1.6; } +.talent-wrap { background:var(--gray-50); border-radius:16px; padding:56px; margin-top:32px; } +.apply-form .form-group { margin-bottom:20px; } +.apply-form label { display:block; font-size:13px; font-weight:600; color:var(--gray-700); margin-bottom:6px; } +.apply-form input, .apply-form select, .apply-form textarea { + width:100%; padding:12px 14px; border:1px solid var(--gray-200); border-radius:8px; + font-size:14px; font-family:inherit; transition:border-color var(--fast); +} +.apply-form input:focus, .apply-form select:focus, .apply-form textarea:focus { outline:none; border-color:var(--primary); } +.apply-form .form-row { display:grid; grid-template-columns:1fr 1fr; gap:16px; } +.required { color:var(--danger); } +.apply-success { text-align:center; padding:80px 40px; background:var(--gray-50); border-radius:16px; } +.apply-success h3 { font-size:24px; font-weight:800; margin-bottom:16px; } +.apply-success p { font-size:15px; color:var(--gray-600); line-height:1.8; } +@media (max-width:768px) { + .job-card { flex-direction:column; } + .job-meta { align-items:flex-start; flex-direction:row; flex-wrap:wrap; } + .apply-form .form-row { grid-template-columns:1fr; } +} diff --git a/frontend/src/pages/Recruit.jsx b/frontend/src/pages/Recruit.jsx index ce9a8b6..7a5bd48 100644 --- a/frontend/src/pages/Recruit.jsx +++ b/frontend/src/pages/Recruit.jsx @@ -1,37 +1,146 @@ -๏ปฟimport React from "react"; -import "./Common.css"; +import React, { useState } from 'react'; +import { Routes, Route, NavLink } from 'react-router-dom'; +import './Common.css'; +import './Recruit.css'; -const JOBS = [ - { title:"Spring Boot ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž", dept:"๊ฐœ๋ฐœํŒ€", type:"์ •๊ทœ์ง", exp:"๊ฒฝ๋ ฅ 3๋…„ ์ด์ƒ" }, - { title:"React ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž", dept:"๊ฐœ๋ฐœํŒ€", type:"์ •๊ทœ์ง", exp:"๊ฒฝ๋ ฅ 2๋…„ ์ด์ƒ" }, - { title:"AI/ML ์—”์ง€๋‹ˆ์–ด", dept:"AIํŒ€", type:"์ •๊ทœ์ง", exp:"๊ฒฝ๋ ฅ 3๋…„ ์ด์ƒ" }, - { title:"์ธํ”„๋ผ ์šด์˜ ์—”์ง€๋‹ˆ์–ด", dept:"์šด์˜ํŒ€", type:"์ •๊ทœ์ง", exp:"๊ฒฝ๋ ฅ 2๋…„ ์ด์ƒ" }, +const SUB_NAV = [ + { path: '/recruit/jobs', label: '์ฑ„์šฉ๊ณต๊ณ ' }, + { path: '/recruit/welfare', label: '๋ณต๋ฆฌํ›„์ƒ' }, + { path: '/recruit/apply', label: '์ง€์›ํ•˜๊ธฐ' }, ]; -export default function Recruit() { +function SubNav({ title }) { return ( -
+ <>
Recruit -

์ฑ„์šฉ ๊ณต๊ณ 

+

{title}

์ง€์˜ค์ •๋ณด๊ธฐ์ˆ ๊ณผ ํ•จ๊ป˜ AI ์ธํ”„๋ผ ํ˜์‹ ์„ ์ด๋Œ์–ด ๊ฐˆ ์ธ์žฌ๋ฅผ ๋ชจ์‹ญ๋‹ˆ๋‹ค.

+ + + ); +} + +/* โ”€โ”€ ์ฑ„์šฉ๊ณต๊ณ  โ”€โ”€ */ +const JOBS = [ + { + id: 1, title: 'AI/LLM ์—”์ง€๋‹ˆ์–ด', dept: 'AIํŒ€', type: '์ •๊ทœ์ง', exp: '๊ฒฝ๋ ฅ 3๋…„ ์ด์ƒ', + stack: ['Python', 'Ollama', 'LangChain', 'FastAPI'], + desc: 'GUARDiA ITSM์˜ ์˜จํ”„๋ ˆ๋ฏธ์Šค AI ์—”์ง„ ๊ฐœ๋ฐœ. ์ž์—ฐ์–ดโ†’๋ช…๋ น ํŒŒ์‹ฑ, LLM ํŒŒ์ธํŠœ๋‹, RAG ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ•.', + deadline: '2026.06.30', hot: true, + }, + { + id: 2, title: 'Java ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž (Spring Boot)', dept: '๊ฐœ๋ฐœํŒ€', type: '์ •๊ทœ์ง', exp: '๊ฒฝ๋ ฅ 3๋…„ ์ด์ƒ', + stack: ['Java', 'Spring Boot', 'Oracle', 'MyBatis'], + desc: '๊ณต๊ณต๊ธฐ๊ด€ SI/SM ํ”„๋กœ์ ํŠธ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ. ERPยทCRMยทํ–‰์ •์ •๋ณด์‹œ์Šคํ…œ ๊ตฌ์ถ• ๋ฐ ์œ ์ง€๋ณด์ˆ˜.', + deadline: '2026.06.30', hot: true, + }, + { + id: 3, title: 'React ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž', dept: '๊ฐœ๋ฐœํŒ€', type: '์ •๊ทœ์ง', exp: '๊ฒฝ๋ ฅ 2๋…„ ์ด์ƒ', + stack: ['React', 'TypeScript', 'Vite', 'Chart.js'], + desc: 'GUARDiA ITSM ๋ฐ ๊ณ ๊ฐ์‚ฌ ํฌํ„ธ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ. ๊ณต๊ณต๊ธฐ๊ด€ ์›น์ ‘๊ทผ์„ฑ(KWCAG 2.1) ์ค€์ˆ˜ ํ•„์ˆ˜.', + deadline: '2026.06.30', hot: false, + }, + { + id: 4, title: '์ธํ”„๋ผ ์šด์˜ ์—”์ง€๋‹ˆ์–ด (DBA)', dept: '์šด์˜ํŒ€', type: '์ •๊ทœ์ง', exp: '๊ฒฝ๋ ฅ 3๋…„ ์ด์ƒ', + stack: ['Oracle', 'Tibero', 'Linux', 'Shell'], + desc: 'Oracle/Tibero DB ์„ค๊ณ„ยทํŠœ๋‹ยท์ด๊ด€. ์‚ผ์„ฑ์ „์žยท๊ตญ๋ฏผ์—ฐ๊ธˆ๊ธ‰ ๋Œ€ํ˜• DB ์šด์˜ ๊ฒฝํ—˜ ์šฐ๋Œ€.', + deadline: '2026.06.15', hot: false, + }, + { + id: 5, title: 'PM / PL (๊ณต๊ณต SI)', dept: 'PM๋ณธ๋ถ€', type: '์ •๊ทœ์ง', exp: '๊ฒฝ๋ ฅ 5๋…„ ์ด์ƒ', + stack: ['PMP', 'PMBOK', 'MS Project', 'Jira'], + desc: '๊ณต๊ณต๊ธฐ๊ด€ ์ •๋ณดํ™”์‚ฌ์—… PM/PL. ํ—Œ๋ฒ•์žฌํŒ์†Œยท๊ตญ๋ฏผ์—ฐ๊ธˆยท์‹œ๋ฆฝ๋Œ€ ์ˆ˜์ค€ ํ”„๋กœ์ ํŠธ ๊ด€๋ฆฌ ๊ฒฝํ—˜ ๋ณด์œ ์ž.', + deadline: '2026.06.15', hot: false, + }, + { + id: 6, title: 'DevOps / CIยทCD ์—”์ง€๋‹ˆ์–ด', dept: '๊ฐœ๋ฐœํŒ€', type: '์ •๊ทœ์ง', exp: '๊ฒฝ๋ ฅ 2๋…„ ์ด์ƒ', + stack: ['Docker', 'Kubernetes', 'Jenkins', 'GitHub Actions'], + desc: 'GUARDiA Vibe CD ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ• ๋ฐ ์šด์˜. ํ์‡„๋ง ํ™˜๊ฒฝ GitOps ๊ฒฝํ—˜ ์šฐ๋Œ€.', + deadline: '์ƒ์‹œ', hot: false, + }, +]; + +function Jobs() { + const [selected, setSelected] = useState(null); + if (selected) { + const j = JOBS.find(j => j.id === selected); + return ( +
+ +
+
+ +
+
+ {j.hot && HOT} +

{j.title}

+
+ {j.dept} + {j.type} + ๊ฒฝ๋ ฅ: {j.exp} + ๋งˆ๊ฐ: {j.deadline} +
+
+
+

์—…๋ฌด ๋‚ด์šฉ

+

{j.desc}

+

๊ธฐ์ˆ  ์Šคํƒ

+
+ {j.stack.map((s, i) => ( + {s} + ))} +
+

์ง€์› ๋ฐฉ๋ฒ•

+

์ด๋ ฅ์„œ ๋ฐ ํฌํŠธํด๋ฆฌ์˜ค๋ฅผ recruit@zioinfo.co.kr ๋กœ ์ œ์ถœํ•˜์‹œ๊ฑฐ๋‚˜, ์•„๋ž˜ ์ง€์›ํ•˜๊ธฐ ๋ฒ„ํŠผ์„ ์ด์šฉํ•ด ์ฃผ์‹ญ์‹œ์˜ค.

+ ์ง€์›ํ•˜๊ธฐ โ†’ +
+
+
+
+ ); + } + return ( +
+
-
- {JOBS.map((j,i) => ( -
-
-

{j.title}

-
+
+ Open Positions +

ํ˜„์žฌ ์ฑ„์šฉ ์ค‘์ธ ํฌ์ง€์…˜

+
+
+ {JOBS.map(j => ( +
setSelected(j.id)}> +
+
+ {j.hot && HOT} {j.dept} {j.type} - {j.exp} +
+

{j.title}

+

{j.desc}

+
+ {j.stack.map((s, i) => {s})}
- ์ง€์›ํ•˜๊ธฐ +
+
๊ฒฝ๋ ฅ{j.exp}
+
๋งˆ๊ฐ{j.deadline}
+ +
))}
@@ -40,3 +149,169 @@ export default function Recruit() {
); } + +/* โ”€โ”€ ๋ณต๋ฆฌํ›„์ƒ โ”€โ”€ */ +const WELFARE = [ + { + cat: '๐Ÿ’ผ ๊ทผ๋ฌดํ™˜๊ฒฝ', items: [ + { icon: '๐Ÿ•˜', name: '์œ ์—ฐ๊ทผ๋ฌด์ œ', desc: '์ฝ”์–ดํƒ€์ž„(10์‹œ~16์‹œ) ์™ธ ์ž์œ ๋กœ์šด ์ถœํ‡ด๊ทผ ์‹œ๊ฐ„ ์„ ํƒ' }, + { icon: '๐Ÿ ', name: '์žฌํƒ๊ทผ๋ฌด', desc: '์ง๋ฌด์— ๋”ฐ๋ผ ์ฃผ 1~2ํšŒ ์žฌํƒ๊ทผ๋ฌด ์ง€์›' }, + { icon: '๐Ÿ’ป', name: '์žฅ๋น„ ์ง€์›', desc: '๋งฅ๋ถ ๋˜๋Š” ๊ณ ์„ฑ๋Šฅ ์œˆ๋„์šฐ ๋…ธํŠธ๋ถ ์„ ํƒ ์ง€๊ธ‰' }, + { icon: '๐ŸŽฏ', name: '๋ชฉํ‘œ ๊ด€๋ฆฌ(OKR)', desc: '๋ถ„๊ธฐ๋ณ„ OKR๋กœ ๋ช…ํ™•ํ•œ ๋ชฉํ‘œยท์„ฑ๊ณผ ๊ด€๋ฆฌ' }, + ] + }, + { + cat: '๐Ÿ“š ์„ฑ์žฅ ์ง€์›', items: [ + { icon: '๐Ÿ“–', name: '๊ต์œก๋น„ ์ง€์›', desc: '์—ฐ 200๋งŒ์› ๊ต์œก๋น„ ์ง€์› (๋„์„œ, ๊ฐ•์˜, ์„ธ๋ฏธ๋‚˜)' }, + { icon: '๐Ÿ†', name: '์ž๊ฒฉ์ฆ ์ง€์›', desc: '์ •๋ณด์ฒ˜๋ฆฌ๊ธฐ์‚ฌ, PMP, AWS, Oracle ์ž๊ฒฉ์ฆ ์ทจ๋“ ์ง€์›' }, + { icon: '๐ŸŽ“', name: '์‚ฌ๋‚ด ๊ฐ•์˜', desc: 'AIยทํด๋ผ์šฐ๋“œยท๋ณด์•ˆ ์›” 1ํšŒ ์‚ฌ๋‚ด ๊ธฐ์ˆ  ์„ธ๋ฏธ๋‚˜' }, + { icon: 'โœˆ๏ธ', name: '์ปจํผ๋Ÿฐ์Šค', desc: 'AWS re:Invent, Google I/O ๋“ฑ ๊ตญ๋‚ด์™ธ ์ปจํผ๋Ÿฐ์Šค ์ฐธ๊ฐ€ ์ง€์›' }, + ] + }, + { + cat: '๐ŸŽ ๋ณต์ง€ ํ˜œํƒ', items: [ + { icon: '๐Ÿฅ', name: '๊ฑด๊ฐ•๊ฒ€์ง„', desc: '์—ฐ 1ํšŒ ์ข…ํ•ฉ๊ฑด๊ฐ•๊ฒ€์ง„ (๋ฐฐ์šฐ์ž ํฌํ•จ)' }, + { icon: '๐ŸŽ‚', name: '๊ฒฝ์กฐ์‚ฌ ์ง€์›', desc: '๊ฒฝ์กฐ๊ธˆยท๊ฒฝ์กฐํœด๊ฐ€ ์ œ๊ณต (๊ฒฐํ˜ผ, ์ถœ์‚ฐ, ์ƒ์กฐ)' }, + { icon: '๐Ÿฝ๏ธ', name: '์‹๋Œ€ ์ง€์›', desc: '์ ์‹ฌ ์‹๋Œ€ ์›” 15๋งŒ์› ์ง€์› (์‹๊ถŒ ๋˜๋Š” ์นด๋“œ)' }, + { icon: '๐ŸŽ‰', name: '๋ช…์ ˆ ์„ ๋ฌผ', desc: '์„คยท์ถ”์„ ๋ช…์ ˆ ์„ ๋ฌผ ๋ฐ ์ƒ์—ฌ๊ธˆ ์ง€๊ธ‰' }, + ] + }, +]; + +function Welfare() { + return ( +
+ +
+
+
+ Welfare +

ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ํ™˜๊ฒฝ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค

+

๊ตฌ์„ฑ์›์ด ์ตœ๊ณ ์˜ ์—ญ๋Ÿ‰์„ ๋ฐœํœ˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹ค์–‘ํ•œ ์ง€์›์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค

+
+ {WELFARE.map((w, wi) => ( +
+

{w.cat}

+
+ {w.items.map((item, i) => ( +
+
{item.icon}
+

{item.name}

+

{item.desc}

+
+ ))} +
+
+ ))} + + {/* ์ธ์žฌ์ƒ */} +
+
+ Talent +

์šฐ๋ฆฌ๊ฐ€ ์ฐพ๋Š” ์ธ์žฌ

+
+
+ {[ + { icon: '๐Ÿ”ฅ', title: '๋„์ „ํ•˜๋Š” ์ธ์žฌ', desc: '์ƒˆ๋กœ์šด ๊ธฐ์ˆ ๊ณผ ๋ฌธ์ œ์— ๋‘๋ ค์›€ ์—†์ด ๋„์ „ํ•˜๋Š” ๋ถ„' }, + { icon: '๐Ÿค', title: 'ํ˜‘๋ ฅํ•˜๋Š” ์ธ์žฌ', desc: 'ํŒ€๊ณผ ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋ฉฐ ์ง€์‹์„ ๋‚˜๋ˆ„๋Š” ๋ถ„' }, + { icon: '๐ŸŽฏ', title: '์ฑ…์ž„์ง€๋Š” ์ธ์žฌ', desc: '๋งก์€ ์—…๋ฌด์— ์˜ค๋„ˆ์‹ญ์„ ๊ฐ–๊ณ  ๋๊นŒ์ง€ ์™„์ˆ˜ํ•˜๋Š” ๋ถ„' }, + ].map((t, i) => ( +
+
{t.icon}
+

{t.title}

+

{t.desc}

+
+ ))} +
+
+
+
+
+ ); +} + +/* โ”€โ”€ ์ง€์›ํ•˜๊ธฐ โ”€โ”€ */ +function Apply() { + const [form, setForm] = useState({ name:'', email:'', phone:'', position:'', exp:'', portfolio:'', message:'' }); + const [status, setStatus] = useState(null); + const handleChange = e => setForm(f => ({ ...f, [e.target.name]: e.target.value })); + const handleSubmit = e => { + e.preventDefault(); + setStatus('success'); + }; + return ( +
+ +
+
+
+ Apply +

์ž…์‚ฌ ์ง€์›์„œ

+

์•„๋ž˜ ์–‘์‹์„ ์ž‘์„ฑํ•˜์‹œ๊ฑฐ๋‚˜ recruit@zioinfo.co.kr๋กœ ์ด๋ ฅ์„œ๋ฅผ ๋ณด๋‚ด์ฃผ์„ธ์š”

+
+ {status === 'success' ? ( +
+
โœ…
+

์ง€์›์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!

+

๊ฒ€ํ†  ํ›„ ์˜์—…์ผ ๊ธฐ์ค€ 3~5์ผ ๋‚ด์— ์—ฐ๋ฝ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.
recruit@zioinfo.co.kr ๋กœ๋„ ์ด๋ ฅ์„œ๋ฅผ ์ถ”๊ฐ€ ์ œ์ถœํ•˜์‹œ๋ฉด ๋”์šฑ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

+ +
+ ) : ( +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ +