This is the complete fix for the missing /admin/ route and frontend serving: Backend: - internal/web/web.go: new package with go:embed for web/dist/ - internal/api/router.go: add routes for /admin/, /admin/*, /assets/* - internal/db/db.go: fix SQLite DSN parsing (sqlite:///path -> path) Build system: - Makefile: new 'embed-prep' target copies web/dist to internal/web/dist - make build now runs embed-prep -> frontend/build automatically Deployment: - deploy/llamalink.service: remove invalid --host/--port flags, add EnvironmentFile=/etc/llamalink/env Verified: - /health returns 200 - /admin/ serves Vue SPA HTML - /assets/* serves CSS and JS files from embedded FS - sqlite:///./llamalink.db works correctly
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
||||
import{d as I,q as M,c as a,a as e,b as p,e as x,x as w,g as c,t as o,F as g,i as C,w as P,f as V,v as j,r,s as k,o as l}from"./index-r2SG-Kf3.js";import{P as T}from"./plus-Bfa9PGWP.js";import{c as _}from"./createLucideIcon-CUrbWv4G.js";/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const $=_("CopyIcon",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]);/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const D=_("Trash2Icon",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]]),F={class:"flex items-center justify-between mb-6"},L={key:0,class:"text-text-muted"},S={key:1,class:"card mb-6 bg-success/5 border-success/20"},B={class:"flex items-center justify-between"},q={class:"mt-4 p-3 bg-background rounded-lg font-mono text-sm break-all"},z={class:"card"},U={class:"table"},E={class:"font-mono text-text-muted"},H=["onClick"],O={key:1,class:"badge badge-info"},Y={key:0},G={key:2,class:"fixed inset-0 bg-black/50 flex items-center justify-center z-50"},J={class:"card w-full max-w-md"},Q={class:"mb-4"},R={class:"flex gap-3 justify-end"},te=I({__name:"ApiKeys",setup(W){const y=r([]),m=r(!0),u=r(!1),d=r(""),i=r(null),v=r(!1);async function b(){m.value=!0;try{const s=await k.get("/api/v1/admin/keys");y.value=s.data}catch(s){console.error("Failed to fetch keys:",s)}finally{m.value=!1}}async function K(){try{const s=await k.post("/api/v1/admin/keys",{name:d.value});i.value=s.data,u.value=!1,d.value="",await b()}catch(s){console.error("Failed to create key:",s)}}async function N(s){if(confirm("Are you sure you want to revoke this key?"))try{await k.delete(`/api/v1/admin/keys/${s}`),await b()}catch(t){console.error("Failed to revoke key:",t)}}async function A(s){await navigator.clipboard.writeText(s),v.value=!0,setTimeout(()=>v.value=!1,2e3)}function h(s){return s?new Date(s).toLocaleString():"Never"}return M(b),(s,t)=>(l(),a("div",null,[e("div",F,[t[6]||(t[6]=e("h1",{class:"text-2xl font-bold"},"API Keys",-1)),e("button",{onClick:t[0]||(t[0]=n=>u.value=!0),class:"btn btn-primary"},[p(x(T),{class:"w-4 h-4 mr-2"}),t[5]||(t[5]=w(" New Key ",-1))])]),m.value?(l(),a("div",L,"Loading...")):c("",!0),i.value?(l(),a("div",S,[e("div",B,[t[7]||(t[7]=e("div",null,[e("h3",{class:"font-semibold text-success"},"API Key Created"),e("p",{class:"text-sm text-text-muted mt-1"}," Copy this key now. You won't be able to see it again. ")],-1)),e("button",{onClick:t[1]||(t[1]=n=>A(i.value.key)),class:"btn btn-secondary"},[p(x($),{class:"w-4 h-4 mr-2"}),w(" "+o(v.value?"Copied!":"Copy"),1)])]),e("div",q,o(i.value.key),1),e("button",{onClick:t[2]||(t[2]=n=>i.value=null),class:"mt-4 text-sm text-text-muted hover:text-text"}," Close ")])):c("",!0),e("div",z,[e("table",U,[t[9]||(t[9]=e("thead",null,[e("tr",null,[e("th",null,"Name"),e("th",null,"Prefix"),e("th",null,"Scopes"),e("th",null,"Owner"),e("th",null,"Created"),e("th",null,"Last Used"),e("th",null,"Actions")])],-1)),e("tbody",null,[(l(!0),a(g,null,C(y.value,n=>(l(),a("tr",{key:n.id},[e("td",null,o(n.name),1),e("td",E,o(n.key_prefix)+"...",1),e("td",null,[(l(!0),a(g,null,C(n.scopes,f=>(l(),a("span",{key:f,class:"badge mr-1"},o(f),1))),128))]),e("td",null,o(n.owner_label||"-"),1),e("td",null,o(h(n.created_at)),1),e("td",null,o(h(n.last_used_at)),1),e("td",null,[n.is_admin?(l(),a("span",O,"Admin")):(l(),a("button",{key:0,onClick:f=>N(n.id),class:"btn btn-danger btn-sm"},[p(x(D),{class:"w-4 h-4"})],8,H))])]))),128)),y.value.length===0?(l(),a("tr",Y,[...t[8]||(t[8]=[e("td",{colspan:"7",class:"text-center text-text-muted py-8"}," No API keys yet. Create one to get started. ",-1)])])):c("",!0)])])]),u.value?(l(),a("div",G,[e("div",J,[t[12]||(t[12]=e("h2",{class:"text-lg font-semibold mb-4"},"Create API Key",-1)),e("form",{onSubmit:P(K,["prevent"])},[e("div",Q,[t[10]||(t[10]=e("label",{class:"block text-sm font-medium mb-2"},"Key Name",-1)),V(e("input",{"onUpdate:modelValue":t[3]||(t[3]=n=>d.value=n),type:"text",class:"input",placeholder:"My API Key",required:""},null,512),[[j,d.value]])]),e("div",R,[e("button",{type:"button",onClick:t[4]||(t[4]=n=>u.value=!1),class:"btn btn-secondary"}," Cancel "),t[11]||(t[11]=e("button",{type:"submit",class:"btn btn-primary"},"Create",-1))])],32)])])):c("",!0)]))}});export{te as default};
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import{d as A,q as C,c as e,a as t,t as d,F as h,b as _,e as m,n as L,g as b,i as M,r as u,s as v,o as a}from"./index-r2SG-Kf3.js";import{c as y}from"./createLucideIcon-CUrbWv4G.js";import{C as N}from"./cpu-ed4VmMFm.js";import{K as S}from"./key-D7ygKuN6.js";/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const j=y("ActivityIcon",[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]]);/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const D=y("ClockIcon",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["polyline",{points:"12 6 12 12 16 14",key:"68esgv"}]]),F={key:0,class:"text-text-muted"},I={key:1,class:"p-4 bg-error/10 border border-error/20 rounded-lg text-error"},q={class:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8"},R={class:"card"},B={class:"flex items-center gap-4"},K={class:"w-12 h-12 bg-primary/10 rounded-xl flex items-center justify-center"},T={class:"text-2xl font-bold"},V={class:"card"},z={class:"flex items-center gap-4"},P={class:"w-12 h-12 bg-success/10 rounded-xl flex items-center justify-center"},$={class:"text-2xl font-bold"},E={class:"card"},H={class:"flex items-center gap-4"},Y={class:"w-12 h-12 bg-warning/10 rounded-xl flex items-center justify-center"},G={class:"text-2xl font-bold"},J={class:"card"},O={class:"flex items-center gap-4"},Q={class:"w-12 h-12 bg-info/10 rounded-xl flex items-center justify-center"},U={class:"text-2xl font-bold"},W={class:"card mb-8"},X={class:"flex items-center justify-between mb-4"},Z={key:0,class:"mb-4"},tt={class:"text-lg font-mono"},st={key:1,class:"p-3 bg-error/10 border border-error/20 rounded-lg text-error text-sm"},et={class:"card"},at={class:"table"},ot={class:"font-mono"},lt={class:"font-mono text-text-muted"},dt={key:0,class:"badge badge-info"},nt={key:1,class:"badge"},rt={key:0,class:"badge badge-success"},it={key:1,class:"badge"},ct=["onClick"],ut={key:1,class:"badge badge-success"},_t={key:0},bt=A({__name:"Dashboard",setup(mt){const r=u({total_requests:0,total_tokens:0,avg_latency_ms:0,active_keys:0,total_models:0}),l=u({status:"stopped",current_model:null,loaded_at:null,last_error:null}),x=u([]),g=u(!0),i=u("");async function f(){var c,s,o;g.value=!0,i.value="";try{const[n,p,w]=await Promise.all([v.get("/api/v1/admin/dashboard"),v.get("/api/v1/admin/models"),v.get("/api/v1/admin/status")]);r.value=n.data.stats,x.value=p.data.data,l.value=w.data}catch(n){i.value=((o=(s=(c=n.response)==null?void 0:c.data)==null?void 0:s.error)==null?void 0:o.message)||"Failed to load dashboard"}finally{g.value=!1}}async function k(c){var s,o,n;try{await v.post(`/api/v1/admin/models/${c}/load`),await f()}catch(p){i.value=((n=(o=(s=p.response)==null?void 0:s.data)==null?void 0:o.error)==null?void 0:n.message)||"Failed to load model"}}return C(f),(c,s)=>(a(),e("div",null,[s[9]||(s[9]=t("h1",{class:"text-2xl font-bold mb-6"},"Dashboard",-1)),g.value?(a(),e("div",F,"Loading...")):i.value?(a(),e("div",I,d(i.value),1)):(a(),e(h,{key:2},[t("div",q,[t("div",R,[t("div",B,[t("div",K,[_(m(j),{class:"w-6 h-6 text-primary"})]),t("div",null,[s[0]||(s[0]=t("p",{class:"text-text-muted text-sm"},"Total Requests",-1)),t("p",T,d(r.value.total_requests.toLocaleString()),1)])])]),t("div",V,[t("div",z,[t("div",P,[_(m(N),{class:"w-6 h-6 text-success"})]),t("div",null,[s[1]||(s[1]=t("p",{class:"text-text-muted text-sm"},"Total Tokens",-1)),t("p",$,d(r.value.total_tokens.toLocaleString()),1)])])]),t("div",E,[t("div",H,[t("div",Y,[_(m(D),{class:"w-6 h-6 text-warning"})]),t("div",null,[s[2]||(s[2]=t("p",{class:"text-text-muted text-sm"},"Avg Latency",-1)),t("p",G,d(r.value.avg_latency_ms.toFixed(0))+"ms",1)])])]),t("div",J,[t("div",O,[t("div",Q,[_(m(S),{class:"w-6 h-6 text-info"})]),t("div",null,[s[3]||(s[3]=t("p",{class:"text-text-muted text-sm"},"Active Keys",-1)),t("p",U,d(r.value.active_keys),1)])])])]),t("div",W,[t("div",X,[s[4]||(s[4]=t("h2",{class:"text-lg font-semibold"},"Model Status",-1)),t("span",{class:L(["badge",{"badge-success":l.value.status==="ready","badge-warning":l.value.status==="loading"||l.value.status==="swapping","badge-error":l.value.status==="failed"}])},d(l.value.status),3)]),l.value.current_model?(a(),e("div",Z,[s[5]||(s[5]=t("p",{class:"text-text-muted text-sm"},"Current Model",-1)),t("p",tt,d(l.value.current_model),1)])):b("",!0),l.value.last_error?(a(),e("div",st,d(l.value.last_error),1)):b("",!0)]),t("div",et,[s[8]||(s[8]=t("h2",{class:"text-lg font-semibold mb-4"},"Models",-1)),t("table",at,[s[7]||(s[7]=t("thead",null,[t("tr",null,[t("th",null,"Name"),t("th",null,"Alias"),t("th",null,"Default"),t("th",null,"Status"),t("th",null,"Actions")])],-1)),t("tbody",null,[(a(!0),e(h,null,M(x.value,o=>(a(),e("tr",{key:o.id},[t("td",ot,d(o.name),1),t("td",lt,d(o.alias),1),t("td",null,[o.is_default?(a(),e("span",dt,"Yes")):(a(),e("span",nt,"No"))]),t("td",null,[o.is_active?(a(),e("span",rt,"Active")):(a(),e("span",it,"Inactive"))]),t("td",null,[o.is_active?(a(),e("span",ut,"Loaded")):(a(),e("button",{key:0,onClick:n=>k(o.name),class:"btn btn-primary btn-sm"}," Load ",8,ct))])]))),128)),x.value.length===0?(a(),e("tr",_t,[...s[6]||(s[6]=[t("td",{colspan:"5",class:"text-center text-text-muted py-8"}," No models configured. Add models via the API. ",-1)])])):b("",!0)])])])],64))]))}});export{bt as default};
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import{d as u,u as m,c as d,a as t,F as x,i as y,b as s,e as o,R as g,h as b,o as r,j as f,n as k,k as _,l as v,m as L,p as C,t as w}from"./index-r2SG-Kf3.js";import{c as n}from"./createLucideIcon-CUrbWv4G.js";import{K as I}from"./key-D7ygKuN6.js";import{C as M}from"./cpu-ed4VmMFm.js";/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const V=n("ChartColumnIcon",[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16",key:"c24i48"}],["path",{d:"M18 17V9",key:"2bz60n"}],["path",{d:"M13 17V5",key:"1frdt8"}],["path",{d:"M8 17v-3",key:"17ska0"}]]);/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const D=n("LayoutDashboardIcon",[["rect",{width:"7",height:"9",x:"3",y:"3",rx:"1",key:"10lvy0"}],["rect",{width:"7",height:"5",x:"14",y:"3",rx:"1",key:"16une8"}],["rect",{width:"7",height:"9",x:"14",y:"12",rx:"1",key:"1hutg5"}],["rect",{width:"7",height:"5",x:"3",y:"16",rx:"1",key:"ldoo1y"}]]);/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const R=n("LogOutIcon",[["path",{d:"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4",key:"1uf3rs"}],["polyline",{points:"16 17 21 12 16 7",key:"1gabdz"}],["line",{x1:"21",x2:"9",y1:"12",y2:"12",key:"1uyos4"}]]),B={class:"min-h-screen bg-background"},z={class:"fixed left-0 top-0 h-full w-64 bg-surface border-r border-border flex flex-col"},A={class:"flex-1 p-4 space-y-1"},K={class:"p-4 border-t border-border"},S={class:"ml-64 min-h-screen p-8"},H=u({__name:"Layout",setup(F){const l=v(),i=b(),c=m(),h=[{name:"Dashboard",path:"/admin/",icon:D},{name:"API Keys",path:"/admin/keys",icon:I},{name:"Models",path:"/admin/models",icon:M},{name:"Usage",path:"/admin/usage",icon:V}];function p(){c.logout(),i.push("/admin/login")}return(N,a)=>(r(),d("div",B,[t("aside",z,[a[1]||(a[1]=t("div",{class:"p-6 border-b border-border"},[t("h1",{class:"text-xl font-bold text-primary"},"LlamaLink"),t("p",{class:"text-xs text-text-muted mt-1"},"Admin Panel")],-1)),t("nav",A,[(r(),d(x,null,y(h,e=>s(o(_),{key:e.path,to:e.path,class:k(["flex items-center gap-3 px-4 py-3 rounded-lg transition-colors",o(l).path===e.path||e.path!=="/admin/"&&o(l).path.startsWith(e.path)?"bg-primary/10 text-primary":"text-text-muted hover:text-text hover:bg-border"])},{default:f(()=>[(r(),L(C(e.icon),{class:"w-5 h-5"})),t("span",null,w(e.name),1)]),_:2},1032,["to","class"])),64))]),t("div",K,[t("button",{onClick:p,class:"flex items-center gap-3 w-full px-4 py-3 rounded-lg text-text-muted hover:text-error hover:bg-error/10 transition-colors"},[s(o(R),{class:"w-5 h-5"}),a[0]||(a[0]=t("span",null,"Logout",-1))])])]),t("main",S,[s(o(g))])]))}});export{H as default};
|
||||
+1
@@ -0,0 +1 @@
|
||||
import{d as p,u as f,c as r,a as e,b as v,e as i,w as _,f as b,v as g,t as x,g as h,r as l,o as a,h as y}from"./index-r2SG-Kf3.js";import{K as k}from"./key-D7ygKuN6.js";import"./createLucideIcon-CUrbWv4G.js";const w={class:"min-h-screen bg-background flex items-center justify-center"},L={class:"w-full max-w-md"},A={class:"card"},S={class:"flex items-center gap-3 mb-6"},V={class:"w-12 h-12 bg-primary/10 rounded-xl flex items-center justify-center"},B={key:0,class:"p-3 bg-error/10 border border-error/20 rounded-lg text-error text-sm"},N=["disabled"],T={key:0},j={key:1},M=p({__name:"Login",setup(C){const d=y(),s=f(),o=l(""),n=l("");async function u(){if(!o.value.trim()){n.value="Admin token is required";return}await s.login(o.value)?d.push("/admin/"):n.value=s.error||"Login failed"}return(c,t)=>(a(),r("div",w,[e("div",L,[e("div",A,[e("div",S,[e("div",V,[v(i(k),{class:"w-6 h-6 text-primary"})]),t[1]||(t[1]=e("div",null,[e("h1",{class:"text-2xl font-bold"},"LlamaLink"),e("p",{class:"text-text-muted text-sm"},"Admin Login")],-1))]),e("form",{onSubmit:_(u,["prevent"]),class:"space-y-4"},[e("div",null,[t[2]||(t[2]=e("label",{class:"block text-sm font-medium mb-2"},"Admin Token",-1)),b(e("input",{"onUpdate:modelValue":t[0]||(t[0]=m=>o.value=m),type:"password",class:"input",placeholder:"Enter your admin token",autocomplete:"current-password"},null,512),[[g,o.value]])]),n.value?(a(),r("div",B,x(n.value),1)):h("",!0),e("button",{type:"submit",class:"btn btn-primary w-full",disabled:i(s).loading},[i(s).loading?(a(),r("span",T,"Logging in...")):(a(),r("span",j,"Login"))],8,N)],32)])])]))}});export{M as default};
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import{d as w,q as h,c as a,a as t,b as y,e as x,x as g,g as v,F as M,i as C,w as U,f as d,v as i,y as V,r as m,s as f,o as n,t as u}from"./index-r2SG-Kf3.js";import{P as L}from"./plus-Bfa9PGWP.js";import{c as N}from"./createLucideIcon-CUrbWv4G.js";/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const z=N("UploadIcon",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"17 8 12 3 7 8",key:"t8dd8p"}],["line",{x1:"12",x2:"12",y1:"3",y2:"15",key:"widbto"}]]),A={class:"flex items-center justify-between mb-6"},P={key:0,class:"text-text-muted"},S={class:"card"},q={class:"table"},D={class:"font-mono"},F={class:"font-mono text-text-muted text-sm"},j={class:"font-mono"},$={key:0,class:"badge badge-info"},B={key:1,class:"badge"},I={key:0,class:"badge badge-success"},G={key:1,class:"badge"},T=["onClick"],E={key:1,class:"badge badge-success"},H={key:0},O={key:1,class:"fixed inset-0 bg-black/50 flex items-center justify-center z-50"},J={class:"card w-full max-w-lg"},K={class:"grid grid-cols-2 gap-4"},Q={class:"flex items-center gap-2"},R={class:"flex gap-3 justify-end pt-2"},et=w({__name:"Models",setup(W){const p=m([]),c=m(!0),r=m(!1),s=m({name:"",model_path:"",alias:"",ctx_size:8192,n_gpu_layers:-1,is_default:!1});async function b(){c.value=!0;try{const o=await f.get("/api/v1/models");p.value=o.data.data}catch(o){console.error("Failed to fetch models:",o)}finally{c.value=!1}}async function _(){try{await f.post("/api/v1/models",s.value),r.value=!1,Object.assign(s.value,{name:"",model_path:"",alias:"",ctx_size:8192,n_gpu_layers:-1,is_default:!1}),await b()}catch(o){console.error("Failed to create model:",o)}}async function k(o){try{await f.post(`/api/v1/models/${o}/load`),await b()}catch(e){console.error("Failed to load model:",e)}}return h(b),(o,e)=>(n(),a("div",null,[t("div",A,[e[9]||(e[9]=t("h1",{class:"text-2xl font-bold"},"Models",-1)),t("button",{onClick:e[0]||(e[0]=l=>r.value=!0),class:"btn btn-primary"},[y(x(L),{class:"w-4 h-4 mr-2"}),e[8]||(e[8]=g(" Add Model ",-1))])]),c.value?(n(),a("div",P,"Loading...")):v("",!0),t("div",S,[t("table",q,[e[12]||(e[12]=t("thead",null,[t("tr",null,[t("th",null,"Name"),t("th",null,"Path"),t("th",null,"Alias"),t("th",null,"Context"),t("th",null,"GPU Layers"),t("th",null,"Default"),t("th",null,"Status"),t("th",null,"Actions")])],-1)),t("tbody",null,[(n(!0),a(M,null,C(p.value,l=>(n(),a("tr",{key:l.id},[t("td",D,u(l.name),1),t("td",F,u(l.model_path),1),t("td",j,u(l.alias),1),t("td",null,u(l.ctx_size.toLocaleString()),1),t("td",null,u(l.n_gpu_layers),1),t("td",null,[l.is_default?(n(),a("span",$,"Default")):(n(),a("span",B,"No"))]),t("td",null,[l.is_active?(n(),a("span",I,"Active")):(n(),a("span",G,"Inactive"))]),t("td",null,[l.is_active?(n(),a("span",E,"Loaded")):(n(),a("button",{key:0,onClick:X=>k(l.name),class:"btn btn-primary btn-sm"},[y(x(z),{class:"w-4 h-4 mr-1"}),e[10]||(e[10]=g(" Load ",-1))],8,T))])]))),128)),p.value.length===0?(n(),a("tr",H,[...e[11]||(e[11]=[t("td",{colspan:"8",class:"text-center text-text-muted py-8"}," No models configured. Add one to get started. ",-1)])])):v("",!0)])])]),r.value?(n(),a("div",O,[t("div",J,[e[20]||(e[20]=t("h2",{class:"text-lg font-semibold mb-4"},"Add Model",-1)),t("form",{onSubmit:U(_,["prevent"]),class:"space-y-4"},[t("div",null,[e[13]||(e[13]=t("label",{class:"block text-sm font-medium mb-2"},"Name",-1)),d(t("input",{"onUpdate:modelValue":e[1]||(e[1]=l=>s.value.name=l),type:"text",class:"input",placeholder:"llama-3.2-1b",required:""},null,512),[[i,s.value.name]])]),t("div",null,[e[14]||(e[14]=t("label",{class:"block text-sm font-medium mb-2"},"Model Path",-1)),d(t("input",{"onUpdate:modelValue":e[2]||(e[2]=l=>s.value.model_path=l),type:"text",class:"input",placeholder:"/models/llama-3.2-1b.q4_k_m.gguf",required:""},null,512),[[i,s.value.model_path]])]),t("div",null,[e[15]||(e[15]=t("label",{class:"block text-sm font-medium mb-2"},"Alias",-1)),d(t("input",{"onUpdate:modelValue":e[3]||(e[3]=l=>s.value.alias=l),type:"text",class:"input",placeholder:"llama-3.2-1b",required:""},null,512),[[i,s.value.alias]])]),t("div",K,[t("div",null,[e[16]||(e[16]=t("label",{class:"block text-sm font-medium mb-2"},"Context Size",-1)),d(t("input",{"onUpdate:modelValue":e[4]||(e[4]=l=>s.value.ctx_size=l),type:"number",class:"input"},null,512),[[i,s.value.ctx_size,void 0,{number:!0}]])]),t("div",null,[e[17]||(e[17]=t("label",{class:"block text-sm font-medium mb-2"},"GPU Layers",-1)),d(t("input",{"onUpdate:modelValue":e[5]||(e[5]=l=>s.value.n_gpu_layers=l),type:"number",class:"input"},null,512),[[i,s.value.n_gpu_layers,void 0,{number:!0}]])])]),t("div",Q,[d(t("input",{"onUpdate:modelValue":e[6]||(e[6]=l=>s.value.is_default=l),type:"checkbox",id:"is_default",class:"w-4 h-4 rounded"},null,512),[[V,s.value.is_default]]),e[18]||(e[18]=t("label",{for:"is_default",class:"text-sm"},"Set as default model",-1))]),t("div",R,[t("button",{type:"button",onClick:e[7]||(e[7]=l=>r.value=!1),class:"btn btn-secondary"}," Cancel "),e[19]||(e[19]=t("button",{type:"submit",class:"btn btn-primary"},"Create",-1))])],32)])])):v("",!0)]))}});export{et as default};
|
||||
+18
File diff suppressed because one or more lines are too long
+6
@@ -0,0 +1,6 @@
|
||||
import{c as e}from"./createLucideIcon-CUrbWv4G.js";/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const h=e("CpuIcon",[["rect",{width:"16",height:"16",x:"4",y:"4",rx:"2",key:"14l7u7"}],["rect",{width:"6",height:"6",x:"9",y:"9",rx:"1",key:"5aljv4"}],["path",{d:"M15 2v2",key:"13l42r"}],["path",{d:"M15 20v2",key:"15mkzm"}],["path",{d:"M2 15h2",key:"1gxd5l"}],["path",{d:"M2 9h2",key:"1bbxkp"}],["path",{d:"M20 15h2",key:"19e6y8"}],["path",{d:"M20 9h2",key:"19tzq7"}],["path",{d:"M9 2v2",key:"165o2o"}],["path",{d:"M9 20v2",key:"i2bqo8"}]]);export{h as C};
|
||||
@@ -0,0 +1,21 @@
|
||||
import{z as a}from"./index-r2SG-Kf3.js";/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const d=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/var o={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round"};/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const h=({size:e,strokeWidth:t=2,absoluteStrokeWidth:r,color:s,iconNode:n,name:i,class:w,...l},{slots:c})=>a("svg",{...o,width:e||o.width,height:e||o.height,stroke:s||o.stroke,"stroke-width":r?Number(t)*24/Number(e):t,class:["lucide",`lucide-${d(i??"icon")}`],...l},[...n.map(u=>a(...u)),...c.default?[c.default()]:[]]);/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const m=(e,t)=>(r,{slots:s})=>a(h,{...r,iconNode:t,name:e},s);export{m as c};
|
||||
+1
File diff suppressed because one or more lines are too long
+38
File diff suppressed because one or more lines are too long
+6
@@ -0,0 +1,6 @@
|
||||
import{c}from"./createLucideIcon-CUrbWv4G.js";/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const a=c("KeyIcon",[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4",key:"g0fldk"}],["path",{d:"m21 2-9.6 9.6",key:"1j0ho8"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5",key:"yqb3hr"}]]);export{a as K};
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import{c as e}from"./createLucideIcon-CUrbWv4G.js";/**
|
||||
* @license lucide-vue-next v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
* See the LICENSE file in the root directory of this source tree.
|
||||
*/const a=e("PlusIcon",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);export{a as P};
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>LlamaLink Admin</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<script type="module" crossorigin src="/assets/index-r2SG-Kf3.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DkKprt_C.css">
|
||||
</head>
|
||||
<body class="bg-background text-text">
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user