@dataramen/cli 0.0.5-4.beta-1 → 0.0.5-4.beta-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/code/server.js
CHANGED
|
@@ -54,4 +54,4 @@
|
|
|
54
54
|
left join pg_class on pg_attribute.attrelid = pg_class.oid
|
|
55
55
|
where
|
|
56
56
|
concat(pg_class.oid, '-', attnum) IN (${e.join(", ")})
|
|
57
|
-
limit 25;`;return(await t.query(r)).rows.reduce((a,n)=>(a[n.row_key]={table:n.relname,column:n.attname},a),{})},st=async(e,t,r)=>{try{console.log(`[PG CONN] Query: ${e}`);let{rows:o,fields:a,command:n,rowCount:i}=await t.query({text:e,rowMode:"array"});if(n==="UPDATE"||n==="INSERT"||n==="DELETE"){if(i!=null&&i>1&&r.allowBulkUpdate!==!0)throw new Error("[PG CONN] Bulk update performed without permission.");return{columns:[{column:"affectedRows",alias:"Affected rows",full:"affectedRows"}],rows:[[i]],query:e}}if(n==="SELECT"){let u=a.map(m=>`'${m.tableID}-${m.columnID}'`),y=await hr(u,t);return{columns:a.map(m=>{let w=y[`${m.tableID}-${m.columnID}`];return{column:w?.column||m.name,alias:m.name,table:w?.table||"",full:w?w.table+"."+w.column:m.name}}),rows:o,query:e}}throw new Error(`[PG CONN] Unsupported command: ${n}`)}catch(o){throw o instanceof s?o:new s(400,o.message)}},gr=async(e,t)=>{await e.query("BEGIN");try{let r=await t();return await e.query("COMMIT"),console.log("[PG CONN] Commit"),r}catch(r){throw await e.query("ROLLBACK"),console.log("[PG CONN] Rollback"),r}},wr=async(e,t)=>{await e.query("BEGIN READ ONLY");try{let r=await t();return console.log("[PG CONN] Read only rollback"),await e.query("ROLLBACK"),r}catch(r){throw console.log("[PG CONN] Rollback"),await e.query("ROLLBACK"),r}},ut=async e=>{let t=await pr(e),r=!1,o=!1,a=async n=>(o||await t.query(`SET search_path TO ${e.schema}`),n());return{dbType:"postgres",dataSource:e,inspectSchema:()=>Tr(e,t),executeQuery:(n,i)=>a(()=>i.type==="SELECT"?wr(t,()=>st(n,t,i)):gr(t,()=>st(n,t,i))),checkConnection:async()=>{},isClosed:()=>r,close:async()=>{if(!r)return r=!0,t.end()}}};var L=async(e,t,r)=>{try{let o;if(t==="mysql")o=await nt(e);else if(t==="postgres")o=await ut(e);else throw new s(500,`Connection manager for ${t} not found`);return r.__connections?r.__connections.push(o):r.__connections=[o],o}catch(o){throw console.error(o),o instanceof s?o:o?.code==="ECONNREFUSED"?new s(500,"Failed to connect to the database"):new s(500,o.message)}};var ee=P(require("node:crypto"));var ct="aes-256-gcm",Sr=12,mt=()=>{let e=p.str("SYMM_ENCRYPTION_KEY");if(!e)throw new Error("Missing ENCRYPTION_KEY in environment variables.");let t=Buffer.from(e,"hex");if(t.length!==32)throw new Error("ENCRYPTION_KEY must be a 64-character hex string (256 bits).");return t},Er=e=>{let t=ee.default.randomBytes(Sr),r=mt(),o=ee.default.createCipheriv(ct,r,t),a=o.update(e,"utf8","hex");a+=o.final("hex");let n=o.getAuthTag();return{encrypted:a,iv:t.toString("hex"),tag:n.toString("hex")}},br=({encrypted:e,iv:t,tag:r})=>{let o=mt(),a=ee.default.createDecipheriv(ct,o,Buffer.from(t,"hex"));a.setAuthTag(Buffer.from(r,"hex"));let n=a.update(e,"hex","utf8");return n+=a.final("utf8"),n},te={encrypt:Er,decrypt:br};var k=(e,t=!1)=>{if(t){let r=te.decrypt({encrypted:e.dbPassword,tag:e.dbPasswordTag,iv:e.dbPasswordIv});return{url:e.dbUrl,user:e.dbUser,database:e.dbDatabase,password:r,port:e.dbPort,schema:e.dbSchema}}return{url:e.dbUrl,user:e.dbUser,database:e.dbDatabase,password:e.dbPassword,port:e.dbPort,schema:e.dbSchema}};var lt=[{value:"=",label:"equals"},{value:"<>",label:"not equal"},{value:">",label:"greater than"},{value:">=",label:"greater than or equal"},{value:"<",label:"less than"},{value:"<=",label:"less than or equal"},{value:"LIKE",label:"contains"},{value:"NOT LIKE",label:"not contains"},{value:"IN",label:"in list"},{value:"NOT IN",label:"not in list"},{value:"IS NULL",label:"is null"},{value:"IS NOT NULL",label:"is not null"}],Rr=lt.reduce((e,t)=>(e[t.value]=t.label,e),{}),na=lt.reduce((e,t)=>(e[t.label]=t.value,e),{}),$=e=>e.map(t=>({label:Rr[t],value:t})),sa=$(["=","<>",">",">=","<","<=","IN","NOT IN","IS NULL","IS NOT NULL"]),ia=$(["=","<>","LIKE","NOT LIKE","IN","NOT IN","IS NULL","IS NOT NULL"]),ua=$(["=","<>","IS NULL","IS NOT NULL"]),ca=$(["=","<>",">",">=","<","<=","IS NULL","IS NOT NULL"]),ma=$(["IS NULL","IS NOT NULL"]),la=$(["IN","NOT IN"]);var dt=["char","varchar","binary","varbinary","blob","text","enum","set","character","character varying","text","citext","uuid","xml","json","jsonb"];var da=["date","datetime","timestamp","timestamptz"].reduce((e,t)=>(e[t]=!0,e),{});var Re=e=>e.fn?e.distinct===!0?`${e.fn} distinct ${e.value}`:`${e.fn} ${e.value}`:e.value;var Ie={read_only:10,editor:20,admin:30,owner:40};var b=e=>{let t=Ie[e];return r=>Ie[r.currentTeamRole]>=t},pt=async e=>{let t=e.routeOptions.config.requireRole;if(t&&!t(e.user))throw new s(403,"You are not authorized to perform this action")};var ft=T(e=>{e.route({method:"get",url:"/:id",handler:async t=>{let{id:r}=f(t),o=await E.findOne({where:{id:r}});if(!o)throw new s(404,"Data source not found");return{data:o}}}),e.route({method:"get",url:"/",handler:async t=>{let{teamId:r}=F(t);return{data:await E.find({where:{team:{id:r}},order:{createdAt:"DESC"}})}}}),e.route({url:"/",method:"post",config:{requireRole:b("admin")},handler:async t=>{let{teamId:r,ownerId:o,...a}=l(t,rt),n=E.create({...a,allowUpdate:!!a.allowUpdate,allowInsert:!!a.allowInsert,team:{id:r},owner:{id:o}}),i=await L(k(n),n.dbType,t);try{await i.checkConnection()}catch{throw new s(400,"Cannot connect to the database, please check datasource configuration")}let{tag:u,iv:y,encrypted:m}=te.encrypt(n.dbPassword);return n.dbPassword=m,n.dbPasswordIv=y,n.dbPasswordTag=u,{data:await E.save(n)}}}),e.route({method:"put",url:"/:id",config:{requireRole:b("admin")},handler:async t=>{let{id:r}=f(t),o=l(t),a=await E.findOneBy({id:r});if(!a)throw new s(404,"Data source not found");let n=E.merge(a,o);return await E.save(n),{data:n}}}),e.route({method:"delete",url:"/:id",config:{requireRole:b("admin")},handler:async(t,r)=>S.transaction(async()=>{let{id:o}=f(t);await Promise.all([U.delete({datasource:{id:o}}),C.delete({dataSource:{id:o}})]),await E.delete({id:o})})}),e.route({method:"post",url:"/:id/inspect",handler:async(t,r)=>{let{id:o}=f(t),a=await E.findOne({where:{id:o},select:["id","dbType","dbDatabase","dbPassword","dbPasswordTag","dbPasswordIv","dbPort","dbUrl","dbSchema","dbUser"]});if(!a)throw new Error("Data source not found");a.status="INSPECTING",await E.save(a);let i=await(await L(k(a,!0),a.dbType,t)).inspectSchema();await U.delete({datasource:{id:o}}),await U.insert(i.sort().map(u=>U.create({tableName:u.tableName,columns:u.columns,datasource:{id:o}}))),a.status="READY",a.lastInspected=new Date,await E.save(a)}}),e.route({method:"get",url:"/:id/inspections",handler:async t=>{let{id:r}=f(t);return{data:await U.find({where:{datasource:{id:r}}})}}})});var H=require("typeorm"),yt=T(e=>{e.route({method:"get",url:"/team/:teamId/datasources",handler:async t=>{let{teamId:r}=f(t);return{data:await E.find({where:{team:{id:r}},order:{name:"ASC"},select:{id:!0,name:!0,updatedAt:!0,dbType:!0,description:!0,allowInsert:!0,allowUpdate:!0}})}}}),e.route({method:"get",url:"/team/:teamId/queries",handler:async t=>{let o=f(t).teamId||t.user.currentTeamId;return{data:(await M.find({where:[{isPersonal:!1,team:{id:o}},{isPersonal:!0,team:{id:o},user:{id:t.user.id}}],relations:{query:!0},select:{id:!0,query:{id:!0,name:!0,updatedAt:!0}}})).map(i=>({name:i.query.name,id:i.query.id,updatedAt:i.query.updatedAt,savedQueryId:i.id}))}}}),e.route({method:"get",url:"/team/:teamId/query",handler:async t=>{let{teamId:r}=f(t),{search:o,size:a,selectedDataSources:n}=F(t),i=o.length>3?parseInt(a)||20:8,u={};n?.length&&(u.id=(0,H.In)(n));let[y,m,w]=await Promise.all([U.find({where:{tableName:(0,H.Like)(`%${o}%`),datasource:u},relations:{datasource:!0},select:{id:!0,tableName:!0,updatedAt:!0,datasource:{name:!0,id:!0}},order:{updatedAt:"ASC"},take:i}),_.find({where:{searchString:(0,H.Like)(`%${o}%`),team:{id:r},user:{id:t.user.id},dataSource:u},relations:{dataSource:!0},select:{id:!0,name:!0,updatedAt:!0,dataSource:{id:!0,name:!0}},order:{updatedAt:"ASC"},take:i}),M.find({where:{searchString:(0,H.Like)(`%${o}%`),team:{id:r},query:{dataSource:u}},relations:{query:{dataSource:!0}},select:{id:!0,updatedAt:!0,query:{id:!0,name:!0,dataSource:{name:!0}}},order:{updatedAt:"ASC"},take:i})]),I=[];return y.forEach(c=>{I.push({name:c.tableName,id:c.id,dataSourceName:c.datasource?.name||"--",dataSourceId:c.datasource?.id||"--",type:"table"})}),m.forEach(c=>{I.push({name:c.name,id:c.id,dataSourceName:c.dataSource?.name||"--",dataSourceId:c.dataSource?.id||"--",type:"tab"})}),w.forEach(c=>{I.push({name:c.query.name,id:c.query.id,dataSourceName:c.query.dataSource?.name||"--",dataSourceId:c.query.dataSource?.id||"--",type:"query"})}),{data:I}}})});var Tt=T(e=>{e.route({method:"get",url:"/:id",handler:async t=>{let{id:r}=f(t),o=await C.findOne({where:{id:r},select:{dataSource:{id:!0}},relations:{dataSource:!0}});return o?{data:o}:{status:404,data:"Query not found"}}}),e.route({method:"post",url:"/",config:{requireRole:b("editor")},handler:async t=>{let r=l(t),o=await E.findOne({where:{id:r.dataSourceId},relations:{team:!0}});return{data:await C.save(C.create({name:r.name,opts:r.opts,team:{id:o?.team.id},dataSource:{id:r.dataSourceId},user:{id:t.user.id}}))}}}),e.route({method:"patch",url:"/:id",config:{requireRole:b("editor")},handler:async t=>{let{id:r}=f(t),o=l(t);if(!(await C.update(r,o)).affected)throw new s(404,"Query not found");return{data:await C.findOneBy({id:r})}}}),e.route({method:"delete",url:"/:id",config:{requireRole:b("editor")},handler:async t=>S.transaction(async()=>{let{id:r}=f(t);if(!(await C.delete({id:r})).affected)return{status:404,data:"Query not found"}})})});var re=e=>{let t=e.distinct===!0?"distinct ":"";return`${e.fn}(${t}${e.value})`},Y={YEAR:e=>`EXTRACT(YEAR FROM ${e.value})`,MONTH:e=>`EXTRACT(MONTH FROM ${e.value})`,DAY:e=>`EXTRACT(DAY FROM ${e.value})`,SUM:e=>`COALESCE(SUM(${e.distinct===!0?"distinct ":""}${e.value}), 0)`,AVG:re,MAX:re,MIN:re,COUNT:re};var oe=e=>{let t=e.distinct===!0?"distinct ":"";return`${e.fn}(${t}${e.value})`},j={YEAR:e=>`YEAR(${e.value})`,MONTH:e=>`MONTH(${e.value})`,DAY:e=>`DAY(${e.value})`,SUM:e=>{let t=e.distinct===!0?"distinct ":"";return`coalesce(${e.fn}(${t}${e.value}), 0)`},AVG:oe,MAX:oe,MIN:oe,COUNT:oe};var ht=["SUM","COUNT","AVG","MAX","MIN"],Ir=["YEAR","MONTH","DAY",...ht],Cr=Ir.reduce((e,t)=>(e[t]=!0,e),{}),Nr=ht.reduce((e,t)=>(e[t]=!0,e),{}),ae=e=>Cr[e],gt=e=>Nr[e],wt=(e,t)=>e.fn&&ae(e.fn)?(t==="postgres"?Y:j)[e.fn](e):e.value;var ne=e=>typeof e=="string",St=e=>{let t="SELECT ";if(e.columns&&e.columns.length>0?t+=e.columns.join(", "):t+="*",e.table&&(t+=` FROM ${e.table}`),e.joins&&e.joins.length>0&&e.joins.forEach(r=>{t+=` ${r.type} JOIN ${r.table} ON ${r.on}`}),e.where&&(t+=` WHERE ${e.where}`),e.groupBy&&e.groupBy.length>0&&(t+=` GROUP BY ${e.groupBy.join(", ")}`),e.having&&(t+=` HAVING ${e.having}`),e.orderBy&&e.orderBy.length>0){let r=e.orderBy.reduce((a,n)=>(a[n.column]=n.direction,a),{}),o=Object.entries(r).map(([a,n])=>`${a} ${n}`);t+=` ORDER BY ${o.join(", ")}`}return e.limit!==void 0&&(t+=` LIMIT ${e.limit}`),e.offset!==void 0&&(t+=` OFFSET ${e.offset}`),t},G=(e,t)=>{let{column:r,operator:o,value:a,fn:n}=e,i=wt({value:r,fn:n},t);switch(o){case"IS NULL":case"IS NOT NULL":return`${i} ${o}`;case"IN":case"NOT IN":let u=a?.map(c=>ne(c.value)?`'${c.value}'`:c.value).join(", ");return`${i} ${o} (${u})`;case"LIKE":return`${i} ${t==="postgres"?"ILIKE":"LIKE"} '%${a?.[0].value}%'`;case"NOT LIKE":return`${i} ${t==="postgres"?"NOT ILIKE":"NOT LIKE"} '%${a?.[0].value}%'`;default:let w=a?.[0],I;return ne(w?.value)&&w?.isColumn!==!0?I=`'${w?.value}'`:I=w?.value,`${i} ${o} ${I}`}};var se=class{constructor(t="mysql"){this.dialect=t,this.skeleton={type:"SELECT"}}addWhere(t){let r=G(t,this.dialect);if(t.isEnabled!==!1)if(this.skeleton.where){let o=t.connector||"AND";this.skeleton.where+=` ${o} ${r}`}else this.skeleton.where=r;return this}addWhereRaw(t,r="AND"){return this.skeleton.where?this.skeleton.where+=` ${r} ${t}`:this.skeleton.where=t,this}clearWhere(){return this.skeleton.where=void 0,this}addHaving(t){let r=G(t,this.dialect);if(t.isEnabled!==!1)if(this.skeleton.having){let o=t.connector||"AND";this.skeleton.having+=` ${o} ${r}`}else this.skeleton.having=r;return this}clearHaving(){return this.skeleton.having=void 0,this}addOrderBy(...t){return this.skeleton.orderBy||(this.skeleton.orderBy=[]),this.skeleton.orderBy.push(...t),this}clearOrderBy(){return this.skeleton.orderBy=void 0,this}setLimit(t){return this.skeleton.limit=t,this}setOffset(t){return this.skeleton.offset=t,this}addGroupBy(t){this.skeleton.groupBy||(this.skeleton.groupBy=[]);let r=this.skeleton.groupBy.findIndex(o=>o===t);return r>-1?this.skeleton.groupBy[r]=t:this.skeleton.groupBy.push(t),this}setTable(t){return this.skeleton.table=t,this}addJoin(...t){return this.skeleton.joins||(this.skeleton.joins=[]),this.skeleton.joins.push(...t),this}selectColumns(t){if(this.skeleton.type!=="SELECT")throw new Error("Column selection is only supported for SELECT queries");return this.skeleton.columns=t,this}toSQL(){return St(this.skeleton)}};var bt=require("typeorm");var ie=async(e,t)=>{let{datasourceId:r,size:o=20,page:a,name:n}=t,{table:i,filters:u,joins:y,groupBy:m,searchAll:w,orderBy:I}=t.opts,c=Dr(t.opts.columns,t.opts.groupBy,t.opts.aggregations),h=await E.findOne({where:{id:r},select:["id","dbType","dbDatabase","dbPassword","dbPasswordTag","dbPasswordIv","dbPort","dbUrl","dbSchema","dbUser"]}),Q=[i],V=[];if(!h)throw new s(404,"Data source not found");let Gt=await C.save(C.create({user:{id:e.user.id},team:{id:e.user.currentTeamId},dataSource:{id:r},name:n,opts:t.opts})),O=new se(h.dbType);O.setTable(i),O.setLimit(o+1),O.setOffset(o*a),u?.forEach(g=>{g.fn&>(g.fn)?O.addHaving(g):O.addWhere(g)}),y&&(O.addJoin(...y),y.forEach(g=>{Q.push(g.table)}));let Oe=_r(c,I,h.dbType);Oe.length>0&&O.addOrderBy(...Oe),m&&m.length>0&&m.forEach(g=>O.addGroupBy(Pr(g,h.dbType)));let Kt=await U.find({where:{tableName:(0,bt.In)(Q),datasource:{id:r}}});for(let g of Kt)if(g.columns)for(let v of g.columns)V.push({column:v.name,table:g.tableName||"",full:`${g.tableName}.${v.name}`,type:v.type});let z;if(c&&c.length>0?z=c.map(g=>Or(g,h.dbType)):z=V.map(g=>`${g.full} as "${g.full}"`),O.selectColumns(z),w){let g=V.filter(v=>dt.includes(v.type)&&z.some(me=>me.startsWith(v.full)));if(g.length>0){let v=g.map(me=>`LOWER(${me.full}) LIKE '%${w.toLowerCase()}%'`);O.addWhereRaw(`(${v.join(" OR ")})`,"AND")}}let ce=await(await L(k(h,!0),h.dbType,e)).executeQuery(O.toSQL(),{type:"SELECT",allowBulkUpdate:!1}),Pe=ce.rows.length>o;return Pe&&ce.rows.pop(),{...ce,queryHistoryId:Gt.id,tables:Q,allColumns:V,hasMore:Pe}},Rt=async(e,t)=>{let r=await E.findOne({where:{id:t.datasourceId},select:["id","dbType","dbDatabase","dbPassword","dbPasswordTag","dbPasswordIv","dbPort","dbUrl","dbSchema","dbUser","allowUpdate"]});if(!r)throw new s(404,"Data source not found");if(!r.allowUpdate)throw new s(403,"This datasource does not allow update operations");let o=t.values.map(({value:u,column:y})=>typeof u=="string"?u&&u.startsWith("=")?`${y}=${u.substring(1)}`:`${y}='${u}'`:`${y}='${u}'`).join(", "),a=t.filters.map(u=>G(u,r.dbType)).join(" AND "),n=`UPDATE ${t.table} SET ${o} WHERE ${a}`;return(await L(k(r,!0),r.dbType,e)).executeQuery(n,{type:"UPDATE",allowBulkUpdate:!1})},It=async(e,t)=>{let r=await E.findOne({where:{id:t.datasourceId},select:["id","dbType","dbDatabase","dbPassword","dbPasswordTag","dbPasswordIv","dbPort","dbUrl","dbSchema","dbUser","allowInsert"]});if(!r)throw new s(404,"Data source not found");if(!r.allowInsert)throw new s(403,"This datasource does not allow insert operations");let{keys:o,values:a}=Ar(t.values),n=`INSERT INTO ${t.table} (${o}) VALUES (${a})`;return(await L(k(r,!0),r.dbType,e)).executeQuery(n,{type:"INSERT",allowBulkUpdate:!1})},Ar=e=>{let t=e.map(({column:o})=>o).join(", "),r=e.map(({value:o})=>typeof o=="string"?o&&o.startsWith("=")?o.substring(1):`'${o}'`:o).join(", ");return{keys:t,values:r}},Or=(e,t)=>{if(e.fn){if(ae(e.fn))return`${(t==="postgres"?Y:j)[e.fn](e)} as "${Re(e)}"`;throw new Error("Function not allowed: "+e.fn)}return`${e.value} as "${e.value}"`},Pr=(e,t)=>{if(e.fn){if(ae(e.fn))return(t==="postgres"?Y:j)[e.fn]({...e,value:Et(e.value,t)});throw new Error("Function not allowed: "+e.fn)}return Et(e.value,t)},Ce=(e,t)=>t==="postgres"?`"${e}"`:t==="mysql"?`\`${e}\``:e,Et=(e,t)=>{let[r,o]=e.split(".");return Ce(r,t)+"."+Ce(o,t)},_r=(e,t,r)=>{if(e&&e.length>0){let o=e.reduce((a,n)=>(a.set(Re(n),{isFn:!!(n.fn||n.distinct)}),a),new Map);t=t.filter(a=>o.has(a.column)).map(a=>o.get(a.column)?.isFn?{...a,column:Ce(a.column,r)}:a)}return t},Dr=(e,t,r)=>{let o=[];return t.length>0||r.length>0?o.push(...t,...r):e.length>0&&o.push(...e),o};var Ct=e=>{},Ur=["--",";","DROP","drop"],Nt=e=>{if(ne(e.value)&&e.value.startsWith("=")){let t=e.value;Ur.forEach(r=>{if(t.includes(r))throw new s(400,"Invalid input value for "+e.column)})}},At=e=>{if(!e.table)throw new s(400,"Table is required");e.values.forEach(Nt)},Ot=e=>{if(!e.table)throw new s(400,"Table is required");e.values.forEach(Nt)};var Pt=T(e=>{e.route({method:"post",url:"/select",handler:async t=>{let r=l(t,Ct);return{data:await ie(t,r)}}}),e.route({method:"post",url:"/insert",config:{requireRole:b("editor")},handler:async t=>{let r=l(t,At);return{data:await It(t,r)}}}),e.route({method:"post",url:"/update",config:{requireRole:b("editor")},handler:async t=>{let r=l(t,Ot);return{data:await Rt(t,r)}}})});var _t=T(e=>{e.get("/",{config:{isPublic:!0}},async()=>({data:{active:!0,version:p.str("SERVER_VERSION")}}))});var Dt=T(e=>{e.route({method:"get",url:"/:id/users",handler:async t=>{let{id:r}=f(t),o=await x.findOne({where:{id:r},relations:{users:{user:!0}}});if(!o)throw new s(404,"Team not found");return{data:o.users.map(a=>({role:a.role,id:a.user.id,name:a.user.username}))}}}),e.route({method:"post",url:"/",config:{requireRole:b("editor")},handler:async t=>S.transaction(async()=>{let r=t.user.id,o=l(t),a=R.create();a.id=r;let n=x.create(o);await x.save(n);let i=N.create({user:a,team:n});return await N.save(i),{data:n}})}),e.route({method:"patch",url:"/:id/user-role",config:{requireRole:b("admin")},handler:async t=>{let{id:r}=f(t),{role:o,userId:a}=l(t,({role:i})=>{if(i==="owner")throw new s(400,"Only one owner is allowed")});if((await N.findOneBy({user:{id:a},team:{id:r}}))?.role==="owner")throw new s(400,"Cannot change owner role");await N.update({user:{id:a},team:{id:r}},{role:o})}}),e.route({method:"delete",url:"/:id",config:{requireRole:b("admin")},handler:async t=>S.transaction(async()=>{let{id:r}=f(t),{userId:o}=F(t);if((await N.findOneBy({user:{id:o},team:{id:r}}))?.role==="owner")throw new s(400,"Cannot delete team owner");await R.update(o,{currentTeam:null}),await N.delete({user:{id:o},team:{id:r}}),await R.delete({id:o})})})});var Ne=P(require("bcryptjs")),K=async e=>{let t=await Ne.default.genSalt(10);return Ne.default.hash(e,t)};var Ut=T(e=>{e.route({method:"get",url:"/",handler:async t=>{let r=await R.findOne({where:{id:t.user.id},relations:{currentTeam:{team:!0}}});if(!r)throw new s(404,"User not found");return{data:{id:r.id,teamId:r.currentTeam?.team.id,teamName:r.currentTeam?.team.name,teamRole:r.currentTeam?.role,username:r.username}}}}),e.route({method:"patch",url:"/",handler:async t=>{let r=t.user.id,o=l(t);if(o.password&&(o.password=await K(o.password)),!(await R.update(r,o)).affected)throw new s(404,"User not found");let n=await R.findOne({where:{id:r},relations:{currentTeam:{team:!0}}});return{data:{id:n?.id,teamId:n?.currentTeam?.team.id,teamName:n?.currentTeam?.team.name,teamRole:n?.currentTeam?.role,username:n?.username}}}}),e.route({method:"post",url:"/",config:{requireRole:b("admin")},handler:async t=>S.transaction(async()=>{let r=l(t),o=await K(r.password),a=await R.save(R.create({username:r.username,password:o})),n=await N.save(N.create({role:"read_only",team:{id:r.teamId},user:{id:a.id}}));await R.update(a.id,{currentTeam:{id:n.id}})})})});var vt=T(e=>{e.route({method:"get",url:"/",handler:async t=>{let r=t.user.id,o=await B.findOneBy({user:{id:r}});return o||(o=await B.save(B.create({user:{id:r}}))),{data:o}}}),e.route({method:"patch",url:"/",handler:async t=>{let{settings:r}=l(t);if(!r.id)throw new s(400,"Settings id is required!");if(!(await B.update(r.id,r)).affected)throw new s(404,"You do not own these settings!");return{data:await B.findOneBy({id:r.id})}}})});function ue(e,...t){let r=[...t];e.searchAll&&r.push(e.searchAll);for(let o of e.filters)if(o.value)for(let a of o.value)a.value&&r.push(a.value.toString());return r.map(o=>o.toLowerCase()).join(",")}var xt=T(e=>{e.route({method:"post",url:"/",config:{requireRole:b("editor")},handler:async t=>{let r=l(t),o=await C.findOne({where:{id:r.queryId}});if(!o)throw new s(400,"Query not found");let a=await M.save(M.create({isPersonal:!1,team:{id:t.user.currentTeamId},user:{id:t.user.id},query:{id:r.queryId},searchString:ue(o.opts,r.name)}));return await C.update(r.queryId,{name:r.name}),{data:a}}}),e.route({method:"delete",url:"/:id",config:{requireRole:b("editor")},handler:async t=>{let{id:r}=f(t);if(!(await M.delete({id:r})).affected)return{status:404,data:"Query not found"}}})});var Mt=e=>{if(!e.queryId&&!(e.opts&&e.name))throw new s(400,"Either queryId or name and opts are required")};var Lt=T(e=>{e.route({method:"get",url:"/",handler:async t=>{let{currentTeamId:r,id:o}=t.user;return{data:(await _.find({where:{team:{id:r},user:{id:o},archived:!1},select:["id","name"]})).map(n=>({name:n.name,id:n.id}))}}}),e.route({method:"get",url:"/:id",handler:async t=>{let{id:r}=f(t),{currentTeamId:o,id:a}=t.user,n=await _.findOne({where:{id:r,team:{id:o},user:{id:a}}});if(!n)throw new s(404,"Not Found");return{data:n}}}),e.route({method:"post",url:"/",handler:async t=>{let{opts:r,name:o,queryId:a}=l(t,Mt),n,i,u=o;if(r)i=r.dataSourceId,n=r;else{let m=await C.findOne({where:{id:a},relations:{dataSource:!0}});if(!m)throw new s(404,"Query not Found");i=m.dataSource.id,n={table:m.opts.table,filters:m.opts.filters,joins:m.opts.joins,orderBy:m.opts.orderBy,columns:m.opts.columns,groupBy:m.opts.groupBy,searchAll:m.opts.searchAll,aggregations:m.opts.aggregations,dataSourceId:m.dataSource.id,page:0,size:50},o||(u=m.name)}return{data:await _.save(_.create({name:u||new Date().toISOString(),opts:n,dataSource:{id:i},user:{id:t.user.id},team:{id:t.user.currentTeamId}}))}}}),e.route({method:"post",url:"/:id/run",handler:async t=>{let{id:r}=f(t),o=l(t),a=await _.findOne({where:{id:r},relations:{user:!0}});if(!a)throw new s(404,"Not found");if(a.user?.id!==t.user.id)throw new s(404,"Not found");return o&&_.update(r,{opts:o,searchString:ue(o,a.name)}),{data:{result:await ie(t,{datasourceId:o.dataSourceId,size:o.size,name:a.name,page:o.page,opts:{table:o.table,filters:o.filters,joins:o.joins,orderBy:o.orderBy,columns:o.columns,groupBy:o.groupBy,searchAll:o.searchAll,aggregations:o.aggregations}})}}}}),e.route({method:"patch",url:"/:id",handler:async t=>{let{id:r}=f(t),o=l(t);if(!await _.findOne({where:{id:r,user:{id:t.user.id}}}))throw new s(404,"Not Found");return await _.update(r,o),{data:{id:r}}}})});var Wt=require("node:path");var vr=e=>e.routeOptions.config.isPublic?!0:!e.url.startsWith("/api/"),kt=async e=>{if(vr(e))return;let t=e.headers.authorization;if(!t)throw new s(401,"Missing auth token");let[r,o]=t.split(" ");try{let{userId:a}=await Je(o),n=await R.findOne({where:{id:a},select:{id:!0,currentTeam:{role:!0,team:{id:!0}}},relations:{currentTeam:{team:!0}}});if(!n)throw new s(401,"User is not part of a team");e.user={id:a,currentTeamId:n.currentTeam.team.id,currentTeamRole:n.currentTeam.role}}catch{throw new s(401,"Unauthorized")}};var qt=(e,t)=>{e.__connections&&e.__connections.forEach(r=>{r.close()})};var Yt=P(require("@fastify/cookie"));var Ae={teamName:"Default Team",username:"admin",password:"admin"},xr=async()=>{let e=await x.findOneBy({});return e||x.save(x.create({name:Ae.teamName}))},Qt=async()=>{let e=await N.findOne({where:{role:"owner"},relations:{user:!0}});if(e)return e.user;let t=await xr(),r=await K(Ae.password),o=await R.save(R.create({username:Ae.username,password:r})),a=await N.save(N.create({user:o,team:t,role:"owner"}));return await R.update(o.id,{currentTeam:a}),o};var A=(0,Bt.default)({querystringParser:e=>Ht.default.parse(e)}),jt=p.num("PORT",4466),Mr=p.str("ALLOWED_ORIGINS","").split(",").map(e=>e.trim()),Lr="0.0.0.0",kr=[`http://localhost:${jt}`,...Mr];function D(e,t){A.register(e,{prefix:t}),console.log("Registered "+t)}(async function(){ve(),await A.register(Yt.default,{}),await A.register(Ft.default,{origin:(t,r)=>{!t||kr.includes(t)?r(null,!0):r(new Error("Not allowed by CORS"),!1)},methods:["GET","POST","PUT","PATCH","DELETE","OPTIONS"],credentials:!0}),await A.register($t.default,{root:(0,Wt.join)(__dirname,"web")}),A.get("/",(t,r)=>{r.sendFile("index.html")}),A.addHook("onRequest",kt),A.addHook("onRequest",pt),A.addHook("onResponse",qt),D(tt,"/api/auth"),D(ft,"/api/data-sources"),D(yt,"/api/project"),D(Tt,"/api/queries"),D(Pt,"/api/runner"),D(_t,"/api/status"),D(Dt,"/api/teams"),D(Ut,"/api/users"),D(vt,"/api/user-settings"),D(xt,"/api/saved-queries"),D(Lt,"/api/workbench-tabs"),A.setNotFoundHandler((t,r)=>{if(t.raw.url?.startsWith("/api/")){r.code(404).send({error:"API route not found"});return}r.sendFile("index.html")}),A.setErrorHandler((t,r,o)=>{if(console.error(t),t instanceof s){o.status(t.status).send({error:t.message});return}else o.status(500).send({error:"Internal Server Error"})}),await A.after(),await je(),await Qt(),A.listen({port:jt,host:Lr},(t,r)=>{t&&(console.error(t),process.exit(1)),console.log(`Server listening at ${r}`)})})();
|
|
57
|
+
limit 25;`;return(await t.query(r)).rows.reduce((a,n)=>(a[n.row_key]={table:n.relname,column:n.attname},a),{})},st=async(e,t,r)=>{try{console.log(`[PG CONN] Query: ${e}`);let{rows:o,fields:a,command:n,rowCount:i}=await t.query({text:e,rowMode:"array"});if(n==="UPDATE"||n==="INSERT"||n==="DELETE"){if(i!=null&&i>1&&r.allowBulkUpdate!==!0)throw new Error("[PG CONN] Bulk update performed without permission.");return{columns:[{column:"affectedRows",alias:"Affected rows",full:"affectedRows"}],rows:[[i]],query:e}}if(n==="SELECT"){let u=a.map(m=>`'${m.tableID}-${m.columnID}'`),y=await hr(u,t);return{columns:a.map(m=>{let w=y[`${m.tableID}-${m.columnID}`];return{column:w?.column||m.name,alias:m.name,table:w?.table||"",full:w?w.table+"."+w.column:m.name}}),rows:o,query:e}}throw new Error(`[PG CONN] Unsupported command: ${n}`)}catch(o){throw o instanceof s?o:new s(400,o.message)}},gr=async(e,t)=>{await e.query("BEGIN");try{let r=await t();return await e.query("COMMIT"),console.log("[PG CONN] Commit"),r}catch(r){throw await e.query("ROLLBACK"),console.log("[PG CONN] Rollback"),r}},wr=async(e,t)=>{await e.query("BEGIN READ ONLY");try{let r=await t();return console.log("[PG CONN] Read only rollback"),await e.query("ROLLBACK"),r}catch(r){throw console.log("[PG CONN] Rollback"),await e.query("ROLLBACK"),r}},ut=async e=>{let t=await pr(e),r=!1,o=!1,a=async n=>(o||await t.query(`SET search_path TO ${e.schema}`),n());return{dbType:"postgres",dataSource:e,inspectSchema:()=>Tr(e,t),executeQuery:(n,i)=>a(()=>i.type==="SELECT"?wr(t,()=>st(n,t,i)):gr(t,()=>st(n,t,i))),checkConnection:async()=>{},isClosed:()=>r,close:async()=>{if(!r)return r=!0,t.end()}}};var L=async(e,t,r)=>{try{let o;if(t==="mysql")o=await nt(e);else if(t==="postgres")o=await ut(e);else throw new s(500,`Connection manager for ${t} not found`);return r.__connections?r.__connections.push(o):r.__connections=[o],o}catch(o){throw console.error(o),o instanceof s?o:o?.code==="ECONNREFUSED"?new s(500,"Failed to connect to the database"):new s(500,o.message)}};var ee=P(require("node:crypto"));var ct="aes-256-gcm",Sr=12,mt=()=>{let e=p.str("SYMM_ENCRYPTION_KEY");if(!e)throw new Error("Missing ENCRYPTION_KEY in environment variables.");let t=Buffer.from(e,"hex");if(t.length!==32)throw new Error("ENCRYPTION_KEY must be a 64-character hex string (256 bits).");return t},Er=e=>{let t=ee.default.randomBytes(Sr),r=mt(),o=ee.default.createCipheriv(ct,r,t),a=o.update(e,"utf8","hex");a+=o.final("hex");let n=o.getAuthTag();return{encrypted:a,iv:t.toString("hex"),tag:n.toString("hex")}},br=({encrypted:e,iv:t,tag:r})=>{let o=mt(),a=ee.default.createDecipheriv(ct,o,Buffer.from(t,"hex"));a.setAuthTag(Buffer.from(r,"hex"));let n=a.update(e,"hex","utf8");return n+=a.final("utf8"),n},te={encrypt:Er,decrypt:br};var k=(e,t=!1)=>{if(t){let r=te.decrypt({encrypted:e.dbPassword,tag:e.dbPasswordTag,iv:e.dbPasswordIv});return{url:e.dbUrl,user:e.dbUser,database:e.dbDatabase,password:r,port:e.dbPort,schema:e.dbSchema}}return{url:e.dbUrl,user:e.dbUser,database:e.dbDatabase,password:e.dbPassword,port:e.dbPort,schema:e.dbSchema}};var lt=[{value:"=",label:"equals"},{value:"<>",label:"not equal"},{value:">",label:"greater than"},{value:">=",label:"greater than or equal"},{value:"<",label:"less than"},{value:"<=",label:"less than or equal"},{value:"LIKE",label:"contains"},{value:"NOT LIKE",label:"not contains"},{value:"IN",label:"in list"},{value:"NOT IN",label:"not in list"},{value:"IS NULL",label:"is null"},{value:"IS NOT NULL",label:"is not null"}],Rr=lt.reduce((e,t)=>(e[t.value]=t.label,e),{}),na=lt.reduce((e,t)=>(e[t.label]=t.value,e),{}),$=e=>e.map(t=>({label:Rr[t],value:t})),sa=$(["=","<>",">",">=","<","<=","IN","NOT IN","IS NULL","IS NOT NULL"]),ia=$(["=","<>","LIKE","NOT LIKE","IN","NOT IN","IS NULL","IS NOT NULL"]),ua=$(["=","<>","IS NULL","IS NOT NULL"]),ca=$(["=","<>",">",">=","<","<=","IS NULL","IS NOT NULL"]),ma=$(["IS NULL","IS NOT NULL"]),la=$(["IN","NOT IN"]);var dt=["char","varchar","binary","varbinary","blob","text","enum","set","character","character varying","text","citext","uuid","xml","json","jsonb"];var da=["date","datetime","timestamp","timestamptz"].reduce((e,t)=>(e[t]=!0,e),{});var Re=e=>e.fn?e.distinct===!0?`${e.fn} distinct ${e.value}`:`${e.fn} ${e.value}`:e.value;var Ie={read_only:10,editor:20,admin:30,owner:40};var b=e=>{let t=Ie[e];return r=>Ie[r.currentTeamRole]>=t},pt=async e=>{let t=e.routeOptions.config.requireRole;if(t&&!t(e.user))throw new s(403,"You are not authorized to perform this action")};var ft=T(e=>{e.route({method:"get",url:"/:id",handler:async t=>{let{id:r}=f(t),o=await E.findOne({where:{id:r}});if(!o)throw new s(404,"Data source not found");return{data:o}}}),e.route({method:"get",url:"/",handler:async t=>{let{teamId:r}=F(t);return{data:await E.find({where:{team:{id:r}},order:{createdAt:"DESC"}})}}}),e.route({url:"/",method:"post",config:{requireRole:b("admin")},handler:async t=>{let{teamId:r,ownerId:o,...a}=l(t,rt),n=E.create({...a,allowUpdate:!!a.allowUpdate,allowInsert:!!a.allowInsert,team:{id:r},owner:{id:o}}),i=await L(k(n),n.dbType,t);try{await i.checkConnection()}catch{throw new s(400,"Cannot connect to the database, please check datasource configuration")}let{tag:u,iv:y,encrypted:m}=te.encrypt(n.dbPassword);return n.dbPassword=m,n.dbPasswordIv=y,n.dbPasswordTag=u,{data:await E.save(n)}}}),e.route({method:"put",url:"/:id",config:{requireRole:b("admin")},handler:async t=>{let{id:r}=f(t),o=l(t),a=await E.findOneBy({id:r});if(!a)throw new s(404,"Data source not found");let n=E.merge(a,o);return await E.save(n),{data:n}}}),e.route({method:"delete",url:"/:id",config:{requireRole:b("admin")},handler:async(t,r)=>S.transaction(async()=>{let{id:o}=f(t);await Promise.all([U.delete({datasource:{id:o}}),C.delete({dataSource:{id:o}})]),await E.delete({id:o})})}),e.route({method:"post",url:"/:id/inspect",handler:async(t,r)=>{let{id:o}=f(t),a=await E.findOne({where:{id:o},select:["id","dbType","dbDatabase","dbPassword","dbPasswordTag","dbPasswordIv","dbPort","dbUrl","dbSchema","dbUser"]});if(!a)throw new Error("Data source not found");a.status="INSPECTING",await E.save(a);let i=await(await L(k(a,!0),a.dbType,t)).inspectSchema();await U.delete({datasource:{id:o}}),await U.insert(i.sort().map(u=>U.create({tableName:u.tableName,columns:u.columns,datasource:{id:o}}))),a.status="READY",a.lastInspected=new Date,await E.save(a)}}),e.route({method:"get",url:"/:id/inspections",handler:async t=>{let{id:r}=f(t);return{data:await U.find({where:{datasource:{id:r}}})}}})});var H=require("typeorm"),yt=T(e=>{e.route({method:"get",url:"/team/:teamId/datasources",handler:async t=>{let{teamId:r}=f(t);return{data:await E.find({where:{team:{id:r}},order:{name:"ASC"},select:{id:!0,name:!0,updatedAt:!0,dbType:!0,description:!0,allowInsert:!0,allowUpdate:!0}})}}}),e.route({method:"get",url:"/team/:teamId/queries",handler:async t=>{let o=f(t).teamId||t.user.currentTeamId;return{data:(await M.find({where:[{isPersonal:!1,team:{id:o}},{isPersonal:!0,team:{id:o},user:{id:t.user.id}}],relations:{query:!0},select:{id:!0,query:{id:!0,name:!0,updatedAt:!0}}})).map(i=>({name:i.query.name,id:i.query.id,updatedAt:i.query.updatedAt,savedQueryId:i.id}))}}}),e.route({method:"get",url:"/team/:teamId/query",handler:async t=>{let{teamId:r}=f(t),{search:o,size:a,selectedDataSources:n}=F(t),i=o.length>3?parseInt(a)||20:8,u={};n?.length&&(u.id=(0,H.In)(n));let[y,m,w]=await Promise.all([U.find({where:{tableName:(0,H.Like)(`%${o}%`),datasource:u},relations:{datasource:!0},select:{id:!0,tableName:!0,datasource:{name:!0,id:!0}},order:{tableName:"ASC"},take:i}),_.find({where:{searchString:(0,H.Like)(`%${o}%`),team:{id:r},user:{id:t.user.id},dataSource:u},relations:{dataSource:!0},select:{id:!0,name:!0,updatedAt:!0,dataSource:{id:!0,name:!0}},order:{updatedAt:"ASC"},take:i}),M.find({where:{searchString:(0,H.Like)(`%${o}%`),team:{id:r},query:{dataSource:u}},relations:{query:{dataSource:!0}},select:{id:!0,updatedAt:!0,query:{id:!0,name:!0,dataSource:{name:!0}}},order:{updatedAt:"ASC"},take:i})]),I=[];return y.forEach(c=>{I.push({name:c.tableName,id:c.id,dataSourceName:c.datasource?.name||"--",dataSourceId:c.datasource?.id||"--",type:"table"})}),m.forEach(c=>{I.push({name:c.name,id:c.id,dataSourceName:c.dataSource?.name||"--",dataSourceId:c.dataSource?.id||"--",type:"tab"})}),w.forEach(c=>{I.push({name:c.query.name,id:c.query.id,dataSourceName:c.query.dataSource?.name||"--",dataSourceId:c.query.dataSource?.id||"--",type:"query"})}),{data:I}}})});var Tt=T(e=>{e.route({method:"get",url:"/:id",handler:async t=>{let{id:r}=f(t),o=await C.findOne({where:{id:r},select:{dataSource:{id:!0}},relations:{dataSource:!0}});return o?{data:o}:{status:404,data:"Query not found"}}}),e.route({method:"post",url:"/",config:{requireRole:b("editor")},handler:async t=>{let r=l(t),o=await E.findOne({where:{id:r.dataSourceId},relations:{team:!0}});return{data:await C.save(C.create({name:r.name,opts:r.opts,team:{id:o?.team.id},dataSource:{id:r.dataSourceId},user:{id:t.user.id}}))}}}),e.route({method:"patch",url:"/:id",config:{requireRole:b("editor")},handler:async t=>{let{id:r}=f(t),o=l(t);if(!(await C.update(r,o)).affected)throw new s(404,"Query not found");return{data:await C.findOneBy({id:r})}}}),e.route({method:"delete",url:"/:id",config:{requireRole:b("editor")},handler:async t=>S.transaction(async()=>{let{id:r}=f(t);if(!(await C.delete({id:r})).affected)return{status:404,data:"Query not found"}})})});var re=e=>{let t=e.distinct===!0?"distinct ":"";return`${e.fn}(${t}${e.value})`},Y={YEAR:e=>`EXTRACT(YEAR FROM ${e.value})`,MONTH:e=>`EXTRACT(MONTH FROM ${e.value})`,DAY:e=>`EXTRACT(DAY FROM ${e.value})`,SUM:e=>`COALESCE(SUM(${e.distinct===!0?"distinct ":""}${e.value}), 0)`,AVG:re,MAX:re,MIN:re,COUNT:re};var oe=e=>{let t=e.distinct===!0?"distinct ":"";return`${e.fn}(${t}${e.value})`},j={YEAR:e=>`YEAR(${e.value})`,MONTH:e=>`MONTH(${e.value})`,DAY:e=>`DAY(${e.value})`,SUM:e=>{let t=e.distinct===!0?"distinct ":"";return`coalesce(${e.fn}(${t}${e.value}), 0)`},AVG:oe,MAX:oe,MIN:oe,COUNT:oe};var ht=["SUM","COUNT","AVG","MAX","MIN"],Ir=["YEAR","MONTH","DAY",...ht],Cr=Ir.reduce((e,t)=>(e[t]=!0,e),{}),Nr=ht.reduce((e,t)=>(e[t]=!0,e),{}),ae=e=>Cr[e],gt=e=>Nr[e],wt=(e,t)=>e.fn&&ae(e.fn)?(t==="postgres"?Y:j)[e.fn](e):e.value;var ne=e=>typeof e=="string",St=e=>{let t="SELECT ";if(e.columns&&e.columns.length>0?t+=e.columns.join(", "):t+="*",e.table&&(t+=` FROM ${e.table}`),e.joins&&e.joins.length>0&&e.joins.forEach(r=>{t+=` ${r.type} JOIN ${r.table} ON ${r.on}`}),e.where&&(t+=` WHERE ${e.where}`),e.groupBy&&e.groupBy.length>0&&(t+=` GROUP BY ${e.groupBy.join(", ")}`),e.having&&(t+=` HAVING ${e.having}`),e.orderBy&&e.orderBy.length>0){let r=e.orderBy.reduce((a,n)=>(a[n.column]=n.direction,a),{}),o=Object.entries(r).map(([a,n])=>`${a} ${n}`);t+=` ORDER BY ${o.join(", ")}`}return e.limit!==void 0&&(t+=` LIMIT ${e.limit}`),e.offset!==void 0&&(t+=` OFFSET ${e.offset}`),t},G=(e,t)=>{let{column:r,operator:o,value:a,fn:n}=e,i=wt({value:r,fn:n},t);switch(o){case"IS NULL":case"IS NOT NULL":return`${i} ${o}`;case"IN":case"NOT IN":let u=a?.map(c=>ne(c.value)?`'${c.value}'`:c.value).join(", ");return`${i} ${o} (${u})`;case"LIKE":return`${i} ${t==="postgres"?"ILIKE":"LIKE"} '%${a?.[0].value}%'`;case"NOT LIKE":return`${i} ${t==="postgres"?"NOT ILIKE":"NOT LIKE"} '%${a?.[0].value}%'`;default:let w=a?.[0],I;return ne(w?.value)&&w?.isColumn!==!0?I=`'${w?.value}'`:I=w?.value,`${i} ${o} ${I}`}};var se=class{constructor(t="mysql"){this.dialect=t,this.skeleton={type:"SELECT"}}addWhere(t){let r=G(t,this.dialect);if(t.isEnabled!==!1)if(this.skeleton.where){let o=t.connector||"AND";this.skeleton.where+=` ${o} ${r}`}else this.skeleton.where=r;return this}addWhereRaw(t,r="AND"){return this.skeleton.where?this.skeleton.where+=` ${r} ${t}`:this.skeleton.where=t,this}clearWhere(){return this.skeleton.where=void 0,this}addHaving(t){let r=G(t,this.dialect);if(t.isEnabled!==!1)if(this.skeleton.having){let o=t.connector||"AND";this.skeleton.having+=` ${o} ${r}`}else this.skeleton.having=r;return this}clearHaving(){return this.skeleton.having=void 0,this}addOrderBy(...t){return this.skeleton.orderBy||(this.skeleton.orderBy=[]),this.skeleton.orderBy.push(...t),this}clearOrderBy(){return this.skeleton.orderBy=void 0,this}setLimit(t){return this.skeleton.limit=t,this}setOffset(t){return this.skeleton.offset=t,this}addGroupBy(t){this.skeleton.groupBy||(this.skeleton.groupBy=[]);let r=this.skeleton.groupBy.findIndex(o=>o===t);return r>-1?this.skeleton.groupBy[r]=t:this.skeleton.groupBy.push(t),this}setTable(t){return this.skeleton.table=t,this}addJoin(...t){return this.skeleton.joins||(this.skeleton.joins=[]),this.skeleton.joins.push(...t),this}selectColumns(t){if(this.skeleton.type!=="SELECT")throw new Error("Column selection is only supported for SELECT queries");return this.skeleton.columns=t,this}toSQL(){return St(this.skeleton)}};var bt=require("typeorm");var ie=async(e,t)=>{let{datasourceId:r,size:o=20,page:a,name:n}=t,{table:i,filters:u,joins:y,groupBy:m,searchAll:w,orderBy:I}=t.opts,c=Dr(t.opts.columns,t.opts.groupBy,t.opts.aggregations),h=await E.findOne({where:{id:r},select:["id","dbType","dbDatabase","dbPassword","dbPasswordTag","dbPasswordIv","dbPort","dbUrl","dbSchema","dbUser"]}),Q=[i],V=[];if(!h)throw new s(404,"Data source not found");let Gt=await C.save(C.create({user:{id:e.user.id},team:{id:e.user.currentTeamId},dataSource:{id:r},name:n,opts:t.opts})),O=new se(h.dbType);O.setTable(i),O.setLimit(o+1),O.setOffset(o*a),u?.forEach(g=>{g.fn&>(g.fn)?O.addHaving(g):O.addWhere(g)}),y&&(O.addJoin(...y),y.forEach(g=>{Q.push(g.table)}));let Oe=_r(c,I,h.dbType);Oe.length>0&&O.addOrderBy(...Oe),m&&m.length>0&&m.forEach(g=>O.addGroupBy(Pr(g,h.dbType)));let Kt=await U.find({where:{tableName:(0,bt.In)(Q),datasource:{id:r}}});for(let g of Kt)if(g.columns)for(let v of g.columns)V.push({column:v.name,table:g.tableName||"",full:`${g.tableName}.${v.name}`,type:v.type});let z;if(c&&c.length>0?z=c.map(g=>Or(g,h.dbType)):z=V.map(g=>`${g.full} as "${g.full}"`),O.selectColumns(z),w){let g=V.filter(v=>dt.includes(v.type)&&z.some(me=>me.startsWith(v.full)));if(g.length>0){let v=g.map(me=>`LOWER(${me.full}) LIKE '%${w.toLowerCase()}%'`);O.addWhereRaw(`(${v.join(" OR ")})`,"AND")}}let ce=await(await L(k(h,!0),h.dbType,e)).executeQuery(O.toSQL(),{type:"SELECT",allowBulkUpdate:!1}),Pe=ce.rows.length>o;return Pe&&ce.rows.pop(),{...ce,queryHistoryId:Gt.id,tables:Q,allColumns:V,hasMore:Pe}},Rt=async(e,t)=>{let r=await E.findOne({where:{id:t.datasourceId},select:["id","dbType","dbDatabase","dbPassword","dbPasswordTag","dbPasswordIv","dbPort","dbUrl","dbSchema","dbUser","allowUpdate"]});if(!r)throw new s(404,"Data source not found");if(!r.allowUpdate)throw new s(403,"This datasource does not allow update operations");let o=t.values.map(({value:u,column:y})=>typeof u=="string"?u&&u.startsWith("=")?`${y}=${u.substring(1)}`:`${y}='${u}'`:`${y}='${u}'`).join(", "),a=t.filters.map(u=>G(u,r.dbType)).join(" AND "),n=`UPDATE ${t.table} SET ${o} WHERE ${a}`;return(await L(k(r,!0),r.dbType,e)).executeQuery(n,{type:"UPDATE",allowBulkUpdate:!1})},It=async(e,t)=>{let r=await E.findOne({where:{id:t.datasourceId},select:["id","dbType","dbDatabase","dbPassword","dbPasswordTag","dbPasswordIv","dbPort","dbUrl","dbSchema","dbUser","allowInsert"]});if(!r)throw new s(404,"Data source not found");if(!r.allowInsert)throw new s(403,"This datasource does not allow insert operations");let{keys:o,values:a}=Ar(t.values),n=`INSERT INTO ${t.table} (${o}) VALUES (${a})`;return(await L(k(r,!0),r.dbType,e)).executeQuery(n,{type:"INSERT",allowBulkUpdate:!1})},Ar=e=>{let t=e.map(({column:o})=>o).join(", "),r=e.map(({value:o})=>typeof o=="string"?o&&o.startsWith("=")?o.substring(1):`'${o}'`:o).join(", ");return{keys:t,values:r}},Or=(e,t)=>{if(e.fn){if(ae(e.fn))return`${(t==="postgres"?Y:j)[e.fn](e)} as "${Re(e)}"`;throw new Error("Function not allowed: "+e.fn)}return`${e.value} as "${e.value}"`},Pr=(e,t)=>{if(e.fn){if(ae(e.fn))return(t==="postgres"?Y:j)[e.fn]({...e,value:Et(e.value,t)});throw new Error("Function not allowed: "+e.fn)}return Et(e.value,t)},Ce=(e,t)=>t==="postgres"?`"${e}"`:t==="mysql"?`\`${e}\``:e,Et=(e,t)=>{let[r,o]=e.split(".");return Ce(r,t)+"."+Ce(o,t)},_r=(e,t,r)=>{if(e&&e.length>0){let o=e.reduce((a,n)=>(a.set(Re(n),{isFn:!!(n.fn||n.distinct)}),a),new Map);t=t.filter(a=>o.has(a.column)).map(a=>o.get(a.column)?.isFn?{...a,column:Ce(a.column,r)}:a)}return t},Dr=(e,t,r)=>{let o=[];return t.length>0||r.length>0?o.push(...t,...r):e.length>0&&o.push(...e),o};var Ct=e=>{},Ur=["--",";","DROP","drop"],Nt=e=>{if(ne(e.value)&&e.value.startsWith("=")){let t=e.value;Ur.forEach(r=>{if(t.includes(r))throw new s(400,"Invalid input value for "+e.column)})}},At=e=>{if(!e.table)throw new s(400,"Table is required");e.values.forEach(Nt)},Ot=e=>{if(!e.table)throw new s(400,"Table is required");e.values.forEach(Nt)};var Pt=T(e=>{e.route({method:"post",url:"/select",handler:async t=>{let r=l(t,Ct);return{data:await ie(t,r)}}}),e.route({method:"post",url:"/insert",config:{requireRole:b("editor")},handler:async t=>{let r=l(t,At);return{data:await It(t,r)}}}),e.route({method:"post",url:"/update",config:{requireRole:b("editor")},handler:async t=>{let r=l(t,Ot);return{data:await Rt(t,r)}}})});var _t=T(e=>{e.get("/",{config:{isPublic:!0}},async()=>({data:{active:!0,version:p.str("SERVER_VERSION")}}))});var Dt=T(e=>{e.route({method:"get",url:"/:id/users",handler:async t=>{let{id:r}=f(t),o=await x.findOne({where:{id:r},relations:{users:{user:!0}}});if(!o)throw new s(404,"Team not found");return{data:o.users.map(a=>({role:a.role,id:a.user.id,name:a.user.username}))}}}),e.route({method:"post",url:"/",config:{requireRole:b("editor")},handler:async t=>S.transaction(async()=>{let r=t.user.id,o=l(t),a=R.create();a.id=r;let n=x.create(o);await x.save(n);let i=N.create({user:a,team:n});return await N.save(i),{data:n}})}),e.route({method:"patch",url:"/:id/user-role",config:{requireRole:b("admin")},handler:async t=>{let{id:r}=f(t),{role:o,userId:a}=l(t,({role:i})=>{if(i==="owner")throw new s(400,"Only one owner is allowed")});if((await N.findOneBy({user:{id:a},team:{id:r}}))?.role==="owner")throw new s(400,"Cannot change owner role");await N.update({user:{id:a},team:{id:r}},{role:o})}}),e.route({method:"delete",url:"/:id",config:{requireRole:b("admin")},handler:async t=>S.transaction(async()=>{let{id:r}=f(t),{userId:o}=F(t);if((await N.findOneBy({user:{id:o},team:{id:r}}))?.role==="owner")throw new s(400,"Cannot delete team owner");await R.update(o,{currentTeam:null}),await N.delete({user:{id:o},team:{id:r}}),await R.delete({id:o})})})});var Ne=P(require("bcryptjs")),K=async e=>{let t=await Ne.default.genSalt(10);return Ne.default.hash(e,t)};var Ut=T(e=>{e.route({method:"get",url:"/",handler:async t=>{let r=await R.findOne({where:{id:t.user.id},relations:{currentTeam:{team:!0}}});if(!r)throw new s(404,"User not found");return{data:{id:r.id,teamId:r.currentTeam?.team.id,teamName:r.currentTeam?.team.name,teamRole:r.currentTeam?.role,username:r.username}}}}),e.route({method:"patch",url:"/",handler:async t=>{let r=t.user.id,o=l(t);if(o.password&&(o.password=await K(o.password)),!(await R.update(r,o)).affected)throw new s(404,"User not found");let n=await R.findOne({where:{id:r},relations:{currentTeam:{team:!0}}});return{data:{id:n?.id,teamId:n?.currentTeam?.team.id,teamName:n?.currentTeam?.team.name,teamRole:n?.currentTeam?.role,username:n?.username}}}}),e.route({method:"post",url:"/",config:{requireRole:b("admin")},handler:async t=>S.transaction(async()=>{let r=l(t),o=await K(r.password),a=await R.save(R.create({username:r.username,password:o})),n=await N.save(N.create({role:"read_only",team:{id:r.teamId},user:{id:a.id}}));await R.update(a.id,{currentTeam:{id:n.id}})})})});var vt=T(e=>{e.route({method:"get",url:"/",handler:async t=>{let r=t.user.id,o=await B.findOneBy({user:{id:r}});return o||(o=await B.save(B.create({user:{id:r}}))),{data:o}}}),e.route({method:"patch",url:"/",handler:async t=>{let{settings:r}=l(t);if(!r.id)throw new s(400,"Settings id is required!");if(!(await B.update(r.id,r)).affected)throw new s(404,"You do not own these settings!");return{data:await B.findOneBy({id:r.id})}}})});function ue(e,...t){let r=[...t];e.searchAll&&r.push(e.searchAll);for(let o of e.filters)if(o.value)for(let a of o.value)a.value&&r.push(a.value.toString());return r.map(o=>o.toLowerCase()).join(",")}var xt=T(e=>{e.route({method:"post",url:"/",config:{requireRole:b("editor")},handler:async t=>{let r=l(t),o=await C.findOne({where:{id:r.queryId}});if(!o)throw new s(400,"Query not found");let a=await M.save(M.create({isPersonal:!1,team:{id:t.user.currentTeamId},user:{id:t.user.id},query:{id:r.queryId},searchString:ue(o.opts,r.name)}));return await C.update(r.queryId,{name:r.name}),{data:a}}}),e.route({method:"delete",url:"/:id",config:{requireRole:b("editor")},handler:async t=>{let{id:r}=f(t);if(!(await M.delete({id:r})).affected)return{status:404,data:"Query not found"}}})});var Mt=e=>{if(!e.queryId&&!(e.opts&&e.name))throw new s(400,"Either queryId or name and opts are required")};var Lt=T(e=>{e.route({method:"get",url:"/",handler:async t=>{let{currentTeamId:r,id:o}=t.user;return{data:(await _.find({where:{team:{id:r},user:{id:o},archived:!1},select:["id","name"]})).map(n=>({name:n.name,id:n.id}))}}}),e.route({method:"get",url:"/:id",handler:async t=>{let{id:r}=f(t),{currentTeamId:o,id:a}=t.user,n=await _.findOne({where:{id:r,team:{id:o},user:{id:a}}});if(!n)throw new s(404,"Not Found");return{data:n}}}),e.route({method:"post",url:"/",handler:async t=>{let{opts:r,name:o,queryId:a}=l(t,Mt),n,i,u=o;if(r)i=r.dataSourceId,n=r;else{let m=await C.findOne({where:{id:a},relations:{dataSource:!0}});if(!m)throw new s(404,"Query not Found");i=m.dataSource.id,n={table:m.opts.table,filters:m.opts.filters,joins:m.opts.joins,orderBy:m.opts.orderBy,columns:m.opts.columns,groupBy:m.opts.groupBy,searchAll:m.opts.searchAll,aggregations:m.opts.aggregations,dataSourceId:m.dataSource.id,page:0,size:50},o||(u=m.name)}return{data:await _.save(_.create({name:u||new Date().toISOString(),opts:n,dataSource:{id:i},user:{id:t.user.id},team:{id:t.user.currentTeamId}}))}}}),e.route({method:"post",url:"/:id/run",handler:async t=>{let{id:r}=f(t),o=l(t),a=await _.findOne({where:{id:r},relations:{user:!0}});if(!a)throw new s(404,"Not found");if(a.user?.id!==t.user.id)throw new s(404,"Not found");return o&&_.update(r,{opts:o,searchString:ue(o,a.name)}),{data:{result:await ie(t,{datasourceId:o.dataSourceId,size:o.size,name:a.name,page:o.page,opts:{table:o.table,filters:o.filters,joins:o.joins,orderBy:o.orderBy,columns:o.columns,groupBy:o.groupBy,searchAll:o.searchAll,aggregations:o.aggregations}})}}}}),e.route({method:"patch",url:"/:id",handler:async t=>{let{id:r}=f(t),o=l(t);if(!await _.findOne({where:{id:r,user:{id:t.user.id}}}))throw new s(404,"Not Found");return await _.update(r,o),{data:{id:r}}}})});var Wt=require("node:path");var vr=e=>e.routeOptions.config.isPublic?!0:!e.url.startsWith("/api/"),kt=async e=>{if(vr(e))return;let t=e.headers.authorization;if(!t)throw new s(401,"Missing auth token");let[r,o]=t.split(" ");try{let{userId:a}=await Je(o),n=await R.findOne({where:{id:a},select:{id:!0,currentTeam:{role:!0,team:{id:!0}}},relations:{currentTeam:{team:!0}}});if(!n)throw new s(401,"User is not part of a team");e.user={id:a,currentTeamId:n.currentTeam.team.id,currentTeamRole:n.currentTeam.role}}catch{throw new s(401,"Unauthorized")}};var qt=(e,t)=>{e.__connections&&e.__connections.forEach(r=>{r.close()})};var Yt=P(require("@fastify/cookie"));var Ae={teamName:"Default Team",username:"admin",password:"admin"},xr=async()=>{let e=await x.findOneBy({});return e||x.save(x.create({name:Ae.teamName}))},Qt=async()=>{let e=await N.findOne({where:{role:"owner"},relations:{user:!0}});if(e)return e.user;let t=await xr(),r=await K(Ae.password),o=await R.save(R.create({username:Ae.username,password:r})),a=await N.save(N.create({user:o,team:t,role:"owner"}));return await R.update(o.id,{currentTeam:a}),o};var A=(0,Bt.default)({querystringParser:e=>Ht.default.parse(e)}),jt=p.num("PORT",4466),Mr=p.str("ALLOWED_ORIGINS","").split(",").map(e=>e.trim()),Lr="0.0.0.0",kr=[`http://localhost:${jt}`,...Mr];function D(e,t){A.register(e,{prefix:t}),console.log("Registered "+t)}(async function(){ve(),await A.register(Yt.default,{}),await A.register(Ft.default,{origin:(t,r)=>{!t||kr.includes(t)?r(null,!0):r(new Error("Not allowed by CORS"),!1)},methods:["GET","POST","PUT","PATCH","DELETE","OPTIONS"],credentials:!0}),await A.register($t.default,{root:(0,Wt.join)(__dirname,"web")}),A.get("/",(t,r)=>{r.sendFile("index.html")}),A.addHook("onRequest",kt),A.addHook("onRequest",pt),A.addHook("onResponse",qt),D(tt,"/api/auth"),D(ft,"/api/data-sources"),D(yt,"/api/project"),D(Tt,"/api/queries"),D(Pt,"/api/runner"),D(_t,"/api/status"),D(Dt,"/api/teams"),D(Ut,"/api/users"),D(vt,"/api/user-settings"),D(xt,"/api/saved-queries"),D(Lt,"/api/workbench-tabs"),A.setNotFoundHandler((t,r)=>{if(t.raw.url?.startsWith("/api/")){r.code(404).send({error:"API route not found"});return}r.sendFile("index.html")}),A.setErrorHandler((t,r,o)=>{if(console.error(t),t instanceof s){o.status(t.status).send({error:t.message});return}else o.status(500).send({error:"Internal Server Error"})}),await A.after(),await je(),await Qt(),A.listen({port:jt,host:Lr},(t,r)=>{t&&(console.error(t),process.exit(1)),console.log(`Server listening at ${r}`)})})();
|