@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands
3 lines (2 loc) • 4.73 kB
JavaScript
import{existsSync as l}from"node:fs";import{mkdir as m,readFile as f,writeFile as g}from"node:fs/promises";import{join as s}from"node:path";import{LunoraError as k}from"@lunora/errors";import{u as w}from"../packem_shared/admin-url-Ca-KI3d_.mjs";import{i as b}from"../packem_shared/command-Bjv4VmYA.mjs";import{s as y}from"../packem_shared/resolve-target-vxaXHmvg.mjs";import{runExportCommand as h,runImportCommand as $}from"../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-C5vGgrhK.mjs";const v=".lunora-backups",p="manifest.json",S="/_lunora/admin/pitr",N="__lunora_admin__:getPitrBookmark",_="__lunora_admin__:pitrRestore",O=t=>typeof t=="object"&&t!==null&&typeof t.id=="string"&&typeof t.file=="string",u=async t=>{const o=s(t,p);if(!l(o))return[];let r;try{r=JSON.parse(await f(o,"utf8"))}catch(e){const a=e instanceof Error?e.message:String(e);throw new k("INTERNAL",`backup: ${o} exists but is not valid JSON (${a}) — refusing to overwrite it; fix or remove it manually`,{cause:e})}if(!Array.isArray(r))throw new TypeError(`backup: ${o} exists but is not a JSON array — refusing to overwrite it; fix or remove it manually`);return r.filter(O)},x=async(t,o)=>{await g(s(t,p),`${JSON.stringify(o,void 0,2)}
`,"utf8")},I=async(t,o)=>{await m(o,{recursive:!0});const r=(t.now??(()=>new Date))().toISOString(),e=`lunora-backup-${r.replaceAll(/[.:]/gu,"-")}.ndjson`,a=await h({cwd:t.cwd,fetchImpl:t.fetchImpl,logger:t.logger,out:s(o,e),prod:t.prod,tables:t.tables,token:t.token,url:t.url});if(a.code!==0)return{code:a.code};const i={bytes:a.bytes,createdAt:r,file:e,id:r,rows:a.rows,tables:t.tables},n=await u(o);return n.push(i),await x(o,n),t.logger.success(`backup created: ${e} (${a.rows.toString()} rows, ${a.bytes.toString()} bytes)`),{code:0,entry:i}},A=async(t,o)=>{const r=await u(o);if(r.length===0)return t.logger.info(`no backups found in ${o}`),{code:0};for(const e of r)t.logger.info(`${e.id} ${e.rows.toString()} rows ${e.bytes.toString()} bytes ${e.file}`);return{code:0}},E=async(t,o)=>{const{target:r}=t;if(r===void 0||r.length===0)return t.logger.error("restore requires a backup id or file path. Usage: lunora backup restore <id|file>"),{code:1};const e=(await u(o)).find(i=>i.id===r),a=e?s(o,e.file):r;return l(a)?{code:(await $({cwd:t.cwd,fetchImpl:t.fetchImpl,file:a,logger:t.logger,prod:t.prod,token:t.token,url:t.url,yes:t.yes})).code}:(t.logger.error(`backup not found: ${r}`),{code:1})},T=t=>{const o=t.token??process.env.LUNORA_ADMIN_TOKEN;if(!o){t.logger.error("admin token required — pass --token or set LUNORA_ADMIN_TOKEN");return}if(t.prod&&t.url===void 0){t.logger.error("--prod requires an explicit --url (refusing to target the implicit localhost worker)");return}if(t.restore===!0&&t.at===void 0&&t.bookmark===void 0){t.logger.error("pitr --restore requires --at <time> or --bookmark <bookmark>");return}if(t.restore===!0&&t.prod===!0&&t.yes!==!0){t.logger.error("pitr --restore --prod restores production data in place. Re-run with --yes to confirm.");return}const r=w(t.url,t.logger,t.cwd);if(r===void 0)return;const e=t.pitrFetch??globalThis.fetch;if(typeof e!="function")throw new TypeError("no fetch implementation available — pass pitrFetch or run on Node >= 18");return{fetchImpl:e,requestUrl:`${r}${S}`,token:o}},q=(t,o)=>{const r={};return t.at!==void 0&&(r.time=t.at),o&&t.bookmark!==void 0&&(r.bookmark=t.bookmark),o&&t.restart===!0&&(r.restart=!0),r},J=async t=>{const o=T(t);if(o===void 0)return{code:1};const r=t.restore===!0,e=r?_:N,a=q(t,r),i=r?"restore":"bookmark";t.logger.info(`POST ${o.requestUrl} -> pitr ${i}${t.shard===void 0?"":` (shard "${t.shard}")`}`);const n=await o.fetchImpl(o.requestUrl,{body:JSON.stringify({args:a,functionPath:e,shardKey:t.shard}),headers:{authorization:`Bearer ${o.token}`,"content-type":"application/json"},method:"POST"}),d=await n.text();let c;try{c=JSON.parse(d)}catch{c=d}return t.logger.info(JSON.stringify(c,void 0,2)),{code:n.ok?0:1}},U=async t=>{const o=t.cwd??process.cwd(),r=s(o,t.dir??v);try{return t.subcommand==="create"?await I(t,r):t.subcommand==="list"?await A(t,r):t.subcommand==="pitr"?await J(t):await E(t,r)}catch(e){return t.logger.error(e instanceof Error?e.message:String(e)),{code:1}}},R=t=>t==="create"||t==="list"||t==="pitr"||t==="restore",M=b(({argument:t,cwd:o,logger:r,options:e})=>{const a=t[0];return R(a)?U({at:e.at,bookmark:e.bookmark,cwd:o,dir:e.dir,logger:r,prod:e.prod===!0,restart:e.restart===!0,restore:e.restore===!0,shard:e.shard,subcommand:a,tables:e.tables,target:t[1],token:e.token,url:y({cwd:o,prod:e.prod===!0,url:e.url}),yes:e.yes===!0}):(r.error(`backup: unknown subcommand "${a??""}" — expected create | list | restore | pitr`),{code:1})});export{M as execute,U as runBackupCommand};