@dataramen/cli 0.0.49 → 0.0.50
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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var kt=Object.create;var
|
|
1
|
+
"use strict";var kt=Object.create;var Ie=Object.defineProperty;var $t=Object.getOwnPropertyDescriptor;var Ht=Object.getOwnPropertyNames;var Yt=Object.getPrototypeOf,jt=Object.prototype.hasOwnProperty;var Wt=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Ht(t))!jt.call(e,a)&&a!==r&&Ie(e,a,{get:()=>t[a],enumerable:!(o=$t(t,a))||o.enumerable});return e};var P=(e,t,r)=>(r=e!=null?kt(Yt(e)):{},Wt(t||!e||!e.__esModule?Ie(r,"default",{value:e,enumerable:!0}):r,e));var z=require("dotenv"),J=require("node:path"),Ce=require("node:fs"),Gt=(()=>{try{let e=(0,Ce.readFileSync)((0,J.join)(__dirname,"..","package.json"),"utf8");return JSON.parse(e)}catch{return{version:"0.0.0"}}})(),Ne=[];process.argv[2]&&Ne.push((0,J.resolve)(process.argv[2]));(0,z.config)({path:Ne});(0,z.populate)(process.env,{SERVER_VERSION:Gt.version,APP_DB_TYPE:"sqlite",APP_DB_DATABASE:"<home>/.dataramen/.runtime/db.sqlite3",PROD:"true"},{override:!1});var Kt=["SYMM_ENCRYPTION_KEY","JWT_SECRET","JWT_REFRESH_SECRET"],Ae=()=>{let e=[];for(let t of Kt)process.env[t]||e.push(t);if(e.length>0)throw new Error("Following env variables are required but not provided: "+e.join(", "))};function Vt(e,t=void 0){return process.env[e]||t}function zt(e,t=void 0){let r=process.env[e];if(!r)return t;let o=Number(r);return!isNaN(o)&&r.trim()!==""?o:t}function Jt(e){return process.env[e]==="true"||process.env[e]==="TRUE"||process.env[e]==="1"}var l={str:Vt,num:zt,bool:Jt};var Ss=require("reflect-metadata"),Ut=P(require("fastify")),vt=P(require("@fastify/cors")),xt=P(require("@fastify/static")),Mt=P(require("qs"));var s=class extends Error{constructor(r,o){super(o);this.status=r;this.message=o}};var Le=require("typeorm");var Oe=require("typeorm");var f=l.str("APP_DB_TYPE")==="sqlite"?"datetime":"timestamp";var ie=new Oe.EntitySchema({name:"DatabaseInspection",tableName:"db_inspection",columns:{id:{type:String,unique:!0,primary:!0,generated:"uuid"},tableName:{nullable:!0,type:String},columns:{type:"json",nullable:!0},createdAt:{type:f,default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:f,default:()=>"CURRENT_TIMESTAMP"}},relations:{datasource:{target:()=>"DataSource",type:"many-to-one",joinTable:!1,cascade:!0}}});var Pe=require("typeorm");var ue=new Pe.EntitySchema({name:"Team",tableName:"teams",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},name:{type:String},createdAt:{type:f,default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:f,default:()=>"CURRENT_TIMESTAMP"}},relations:{users:{type:"one-to-many",target:()=>"UsersToTeams",inverseSide:"team"},queries:{type:"one-to-many",target:()=>"Query",inverseSide:"team"},datasources:{type:"one-to-many",target:()=>"DataSource",inverseSide:"team"}}});var _e=require("typeorm");var ce=new _e.EntitySchema({name:"User",tableName:"users",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},createdAt:{type:f,default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:f,default:()=>"CURRENT_TIMESTAMP"},username:{type:String,unique:!0},password:{type:String}},relations:{teams:{type:"one-to-many",target:()=>"UsersToTeams",inverseSide:"user"},settings:{type:"one-to-one",target:()=>"UserSettings",inverseSide:"user"},currentTeam:{type:"one-to-one",target:()=>"UsersToTeams",inverseSide:"user",joinColumn:!0},queries:{type:"one-to-many",target:()=>"Query",inverseSide:"user"}}});var De=require("typeorm");var le=new De.EntitySchema({name:"UserSettings",tableName:"user_settings",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},createdAt:{type:f,default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:f,default:()=>"CURRENT_TIMESTAMP"}},relations:{user:{type:"one-to-one",target:()=>"User",inverseSide:"settings",joinColumn:!0}}});var Ue=require("typeorm");var me=new Ue.EntitySchema({name:"DataSource",tableName:"data_sources",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},dbUrl:{type:String},dbPort:{type:Number,nullable:!0},dbUser:{type:String},dbPassword:{type:String,nullable:!0,select:!1},dbPasswordIv:{type:String,nullable:!0,select:!1},dbPasswordTag:{type:String,nullable:!0,select:!1},dbType:{type:String},createdAt:{type:f,default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:f,default:()=>"CURRENT_TIMESTAMP"},name:{type:String},description:{type:String,nullable:!0},dbDatabase:{type:String},dbSchema:{type:String,nullable:!0},allowInsert:{type:Boolean,default:!1},allowUpdate:{type:Boolean,default:!1},lastInspected:{type:f,nullable:!0,default:null},status:{type:String,nullable:!0}},relations:{team:{type:"many-to-one",target:()=>"Team",inverseSide:"datasources",joinColumn:!0},inspections:{type:"one-to-many",target:()=>"DatabaseInspection",inverseSide:"datasource"},queries:{type:"one-to-many",target:()=>"Query",inverseSide:"dataSource"},owner:{type:"many-to-one",target:()=>"User",joinColumn:!0}}});var qe=P(require("node:os")),Qe=require("node:path");var ve=require("typeorm");var de=new ve.EntitySchema({name:"Query",tableName:"query",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},name:{type:String},opts:{type:"json",default:"{}"},createdAt:{type:f,default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:f,default:()=>"CURRENT_TIMESTAMP",onUpdate:"CURRENT_TIMESTAMP"}},relations:{team:{type:"many-to-one",target:()=>"Team",inverseSide:"queries",joinColumn:!0},dataSource:{type:"many-to-one",target:()=>"DataSource",inverseSide:"datasources",joinColumn:!0},user:{type:"many-to-one",target:()=>"User",inverseSide:"queries",joinColumn:!0,nullable:!0}}});var xe=require("typeorm"),pe=new xe.EntitySchema({name:"UsersToTeams",tableName:"users_to_teams",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},role:{type:"varchar",default:"admin",nullable:!1}},relations:{team:{type:"many-to-one",target:()=>"Team",inverseSide:"users"},user:{type:"many-to-one",target:()=>"User",inverseSide:"teams"}}});var Me=require("typeorm");var fe=new Me.EntitySchema({name:"SavedQuery",tableName:"saved_queries",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},isPersonal:{type:Boolean},createdAt:{type:f,default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:f,default:()=>"CURRENT_TIMESTAMP",onUpdate:"CURRENT_TIMESTAMP"}},relations:{team:{type:"many-to-one",target:()=>"Team",inverseSide:"queries",joinColumn:!0},user:{type:"many-to-one",target:()=>"User",inverseSide:"queries",joinColumn:!0,nullable:!0},query:{type:"one-to-one",target:()=>"Query",joinColumn:!0,nullable:!1}}});function Xt(){let e=l.str("APP_DB_DATABASE");if(!e)throw new Error("Bad value for TYPEORM_DATABASE. Please check your config!");return e.startsWith("<home>")&&(e=e.replace("<home>",qe.default.homedir())),e}var b=new Le.DataSource({type:l.str("APP_DB_TYPE"),database:Xt(),host:l.str("APP_DB_HOST"),username:l.str("APP_DB_USERNAME"),password:l.str("APP_DB_PASSWORD"),port:l.num("APP_DB_PORT"),schema:l.str("APP_DB_SCHEMA"),logging:l.bool("APP_DB_LOGGING"),migrationsRun:!0,migrations:[Qe.posix.join(__dirname,"migrations","*.js")],entities:[ie,me,ue,ce,pe,le,de,fe]}),Fe=async()=>{if(!b.isInitialized)return b.initialize();throw new Error("Already initialized")},_=b.getRepository(ie),w=b.getRepository(me),v=b.getRepository(ue),I=b.getRepository(ce),C=b.getRepository(pe),F=b.getRepository(le),N=b.getRepository(de),x=b.getRepository(fe);var E=e=>(t,r,o)=>{e(t),o()};var H=require("jose");var Be=new TextEncoder,ke=Be.encode(l.str("JWT_SECRET")),$e=Be.encode(l.str("JWT_REFRESH_SECRET")),ye=async({userId:e})=>new H.SignJWT({sub:e}).setProtectedHeader({alg:"HS256"}).setExpirationTime("1h").sign(ke),Te=async({userId:e})=>new H.SignJWT({sub:e}).setProtectedHeader({alg:"HS256"}).setExpirationTime("10d").sign($e),He=async(e,t)=>{try{let{payload:r}=await(0,H.jwtVerify)(e,t);if(!r.sub)throw new s(401,"Failed to verify access token");return{userId:r.sub}}catch(r){throw r instanceof s?r:r instanceof Error?new s(401,r.message):new s(401,"Failed to verify refresh token")}},Ye=async e=>He(e,ke),je=async e=>He(e,$e);var y=(e,t)=>{let r=e.body;return t&&t(r),r},B=(e,t)=>{let r=e.query;return t&&t(r),r},S=(e,t)=>{let r=e.params;return t&&t(r),r};var Ge=P(require("bcryptjs"));var We=e=>{if(!e?.username)throw new s(400,"Username is required");if(!e?.password)throw new s(400,"Password is required")};var X="DATARAMEN_refresh_token",ge={httpOnly:!0,secure:l.bool("PROD"),sameSite:l.bool("PROD"),path:"/",maxAge:10*24*60*60},Ke=E(e=>{e.route({method:"post",url:"/login",config:{isPublic:!0},handler:async(t,r)=>{let{username:o,password:a}=y(t,We),n=await I.findOne({where:{username:o}});if(!n||!Ge.default.compareSync(a,n.password))throw new s(401,"Invalid credentials");let[i,u]=await Promise.all([ye({userId:n?.id}),Te({userId:n?.id})]);return r.setCookie(X,u,ge),{data:{accessToken:i}}}}),e.route({method:"post",url:"/refresh",config:{isPublic:!0},handler:async(t,r)=>{let o=t.cookies[X];if(!o)return r.code(401).send({message:"Missing refresh token"});let{userId:a}=await je(o),[n,i]=await Promise.all([ye({userId:a}),Te({userId:a})]);return r.setCookie(X,i,ge),{data:{accessToken:n}}}}),e.route({method:"post",url:"/logout",config:{isPublic:!0},handler:async(t,r)=>(r.clearCookie(X,ge),{data:!0})})});var Ve=e=>{if(!e.dbUrl)throw new s(400,"url is required");if(!e.dbUser)throw new s(400,"user is required");if(!e.dbType)throw new s(400,"type is required");if(!e.name)throw new s(400,"name is required");if(!e.dbDatabase)throw new s(400,"database is required")};var Je=P(require("mysql2/promise"));var Zt=({database:e,password:t,user:r,url:o})=>Je.default.createConnection({host:o,user:r,database:e,password:t}),er=async e=>{let t=`
|
|
2
2
|
SELECT TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION
|
|
3
3
|
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
|
4
4
|
WHERE CONSTRAINT_NAME = 'PRIMARY'
|
|
@@ -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),{})},Ze=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(c=>`'${c.tableID}-${c.columnID}'`),T=await cr(u,t);return{columns:a.map(c=>{let d=T[`${c.tableID}-${c.columnID}`];return{column:d?.column||c.name,alias:c.name,table:d?.table||"",full:d?d.table+"."+d.column:c.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)}},lr=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}},mr=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}},tt=async e=>{let t=await nr(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:()=>ur(e,t),executeQuery:(n,i)=>a(()=>i.type==="SELECT"?mr(t,()=>Ze(n,t,i)):lr(t,()=>Ze(n,t,i))),checkConnection:async()=>{},isClosed:()=>r,close:async()=>{if(!r)return r=!0,t.end()}}};var M=async(e,t,r)=>{try{let o;if(t==="mysql")o=await Xe(e);else if(t==="postgres")o=await tt(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 Z=P(require("node:crypto"));var rt="aes-256-gcm",dr=12,ot=()=>{let e=l.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},pr=e=>{let t=Z.default.randomBytes(dr),r=ot(),o=Z.default.createCipheriv(rt,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")}},fr=({encrypted:e,iv:t,tag:r})=>{let o=ot(),a=Z.default.createDecipheriv(rt,o,Buffer.from(t,"hex"));a.setAuthTag(Buffer.from(r,"hex"));let n=a.update(e,"hex","utf8");return n+=a.final("utf8"),n},ee={encrypt:pr,decrypt:fr};var L=(e,t=!1)=>{if(t){let r=ee.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 at=[{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"}],yr=at.reduce((e,t)=>(e[t.value]=t.label,e),{}),Ko=at.reduce((e,t)=>(e[t.label]=t.value,e),{}),k=e=>e.map(t=>({label:yr[t],value:t})),Vo=k(["=","<>",">",">=","<","<=","IN","NOT IN","IS NULL","IS NOT NULL"]),zo=k(["=","<>","LIKE","NOT LIKE","IN","NOT IN","IS NULL","IS NOT NULL"]),Jo=k(["=","<>","IS NULL","IS NOT NULL"]),Xo=k(["=","<>",">",">=","<","<=","IS NULL","IS NOT NULL"]),Zo=k(["IS NULL","IS NOT NULL"]),ea=k(["IN","NOT IN"]);var nt=["char","varchar","binary","varbinary","blob","text","enum","set","character","character varying","text","citext","uuid","xml","json","jsonb"];var ta=["date","datetime","timestamp","timestamptz"].reduce((e,t)=>(e[t]=!0,e),{});var he=e=>e.fn?e.distinct===!0?`${e.fn} distinct ${e.value}`:`${e.fn} ${e.value}`:e.value;var Ee={read_only:10,editor:20,admin:30,owner:40};var C=e=>{let t=Ee[e];return r=>Ee[r]>=t},st=async e=>{let t=e.routeOptions.config.requireRole;if(t&&!t(e.user.currentTeamRole))throw new s(403,"You are not authorized to perform this action")};var it=w(e=>{e.route({method:"get",url:"/:id",handler:async t=>{let{id:r}=R(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}=B(t);return{data:await E.find({where:{team:{id:r}},order:{createdAt:"DESC"}})}}}),e.route({url:"/",method:"post",config:{requireRole:C("admin")},handler:async t=>{let{teamId:r,ownerId:o,...a}=y(t,Ve),n=E.create({...a,allowUpdate:!!a.allowUpdate,allowInsert:!!a.allowInsert,team:{id:r},owner:{id:o}}),i=await M(L(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:T,encrypted:c}=ee.encrypt(n.dbPassword);return n.dbPassword=c,n.dbPasswordIv=T,n.dbPasswordTag=u,{data:await E.save(n)}}}),e.route({method:"put",url:"/:id",config:{requireRole:C("admin")},handler:async t=>{let{id:r}=R(t),o=y(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:C("admin")},handler:async(t,r)=>S.transaction(async()=>{let{id:o}=R(t);await Promise.all([_.delete({datasource:{id:o}}),N.delete({dataSource:{id:o}})]),await E.delete({id:o})})}),e.route({method:"post",url:"/:id/inspect",handler:async(t,r)=>{let{id:o}=R(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 M(L(a,!0),a.dbType,t)).inspectSchema();await _.delete({datasource:{id:o}}),await _.insert(i.sort().map(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}=R(t);return{data:await _.find({where:{datasource:{id:r}}})}}})});var $=require("typeorm"),ut=w(e=>{e.route({method:"get",url:"/team/:teamId/datasources",handler:async(t,r)=>{let{teamId:o}=R(t);return{data:await E.find({where:{team:{id:o}},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,r)=>{let a=R(t).teamId||t.user.currentTeamId;return{data:(await x.find({where:[{isPersonal:!1,team:{id:a}},{isPersonal:!0,team:{id:a},user:{id:t.user.id}}],relations:{query:!0},select:{id:!0,query:{id:!0,name:!0,updatedAt:!0}}})).map(u=>({name:u.query.name,id:u.query.id,updatedAt:u.query.updatedAt,savedQueryId:u.id}))}}}),e.route({method:"get",url:"/team/:teamId/query",handler:async t=>{let{teamId:r}=R(t),{search:o,size:a,selectedDataSources:n}=B(t),i=(parseInt(a)||20)/2,u={};n?.length&&(u.id=(0,$.In)(n));let[T,c]=await Promise.all([_.find({where:{tableName:(0,$.Like)(`%${o}%`),datasource:u},relations:{datasource:!0},select:{id:!0,tableName:!0,datasource:{name:!0,id:!0}},order:{tableName:"ASC"},take:i}),x.find({where:[{query:{dataSource:u,name:(0,$.Like)(`%${o}%`)},team:{id:r},isPersonal:!1},{query:{dataSource:u,name:(0,$.Like)(`%${o}%`)},team:{id:r},isPersonal:!0,user:{id:t.user.id}}],relations:{query:{dataSource:!0}},select:{id:!0,query:{id:!0,name:!0,dataSource:{name:!0}}},order:{query:{name:"ASC"}},take:i})]),d=[];return T.forEach(p=>{d.push({name:p.tableName,id:p.id,dataSourceName:p.datasource?.name||"--",dataSourceId:p.datasource?.id||"--",type:"table"})}),c.forEach(p=>{d.push({name:p.query.name,id:p.id,dataSourceName:p.query.dataSource?.name||"--",dataSourceId:p.query.dataSource?.id||"--",type:"query"})}),{data:d}}})});var ct=w(e=>{e.route({method:"get",url:"/:id",handler:async t=>{let{id:r}=R(t),o=await N.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:C("editor")},handler:async t=>{let r=y(t),o=await E.findOne({where:{id:r.dataSourceId},relations:{team:!0}});return{data:await N.save(N.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:C("editor")},handler:async t=>{let{id:r}=R(t),o=y(t);if(!(await N.update(r,o)).affected)throw new s(404,"Query not found");return{data:await N.findOneBy({id:r})}}}),e.route({method:"delete",url:"/:id",config:{requireRole:C("editor")},handler:async t=>S.transaction(async()=>{let{id:r}=R(t);if(!(await N.delete({id:r})).affected)return{status:404,data:"Query not found"}})})});var te=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:te,MAX:te,MIN:te,COUNT:te};var re=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:re,MAX:re,MIN:re,COUNT:re};var lt=["SUM","COUNT","AVG","MAX","MIN"],Tr=["YEAR","MONTH","DAY",...lt],gr=Tr.reduce((e,t)=>(e[t]=!0,e),{}),hr=lt.reduce((e,t)=>(e[t]=!0,e),{}),oe=e=>gr[e],mt=e=>hr[e],dt=(e,t)=>e.fn&&oe(e.fn)?(t==="postgres"?Y:j)[e.fn](e):e.value;var ae=e=>typeof e=="string",pt=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},W=(e,t)=>{let{column:r,operator:o,value:a,fn:n}=e,i=dt({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(m=>ae(m.value)?`'${m.value}'`:m.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 d=a?.[0],p;return ae(d?.value)&&d?.isColumn!==!0?p=`'${d?.value}'`:p=d?.value,`${i} ${o} ${p}`}};var ne=class{constructor(t="mysql"){this.dialect=t,this.skeleton={type:"SELECT"}}addWhere(t){let r=W(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=W(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 pt(this.skeleton)}};var yt=require("typeorm");var Tt=async(e,t)=>{let{datasourceId:r,size:o,page:a,name:n}=t,{table:i,filters:u,joins:T,groupBy:c,searchAll:d,orderBy:p}=t.opts,m=br(t.opts.columns,t.opts.groupBy,t.opts.aggregations),g=await E.findOne({where:{id:r},select:["id","dbType","dbDatabase","dbPassword","dbPasswordTag","dbPasswordIv","dbPort","dbUrl","dbSchema","dbUser"]}),Q=[i],K=[];if(!g)throw new s(404,"Data source not found");let Ft=await N.save(N.create({user:{id:e.user.id},team:{id:e.user.currentTeamId},dataSource:{id:r},name:n,opts:t.opts})),O=new ne(g.dbType);O.setTable(i),O.setLimit(o||20),O.setOffset(o*a),u?.forEach(h=>{h.fn&&mt(h.fn)?O.addHaving(h):O.addWhere(h)}),T&&(O.addJoin(...T),T.forEach(h=>{Q.push(h.table)}));let be=Sr(m,p,g.dbType);be.length>0&&O.addOrderBy(...be),c&&c.length>0&&c.forEach(h=>O.addGroupBy(Rr(h,g.dbType)));let Bt=await _.find({where:{tableName:(0,yt.In)(Q),datasource:{id:r}}});for(let h of Bt)if(h.columns)for(let U of h.columns)K.push({column:U.name,table:h.tableName||"",full:`${h.tableName}.${U.name}`,type:U.type});let V;if(m&&m.length>0?V=m.map(h=>wr(h,g.dbType)):V=K.map(h=>`${h.full} as "${h.full}"`),O.selectColumns(V),d){let h=K.filter(U=>nt.includes(U.type)&&V.some(se=>se.startsWith(U.full)));if(h.length>0){let U=h.map(se=>`LOWER(${se.full}) LIKE '%${d.toLowerCase()}%'`);O.addWhereRaw(`(${U.join(" OR ")})`,"AND")}}return{...await(await M(L(g,!0),g.dbType,e)).executeQuery(O.toSQL(),{type:"SELECT",allowBulkUpdate:!1}),queryHistoryId:Ft.id,tables:Q,allColumns:K}},gt=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:T})=>typeof u=="string"?u&&u.startsWith("=")?`${T}=${u.substring(1)}`:`${T}='${u}'`:`${T}='${u}'`).join(", "),a=t.filters.map(u=>W(u,r.dbType)).join(" AND "),n=`UPDATE ${t.table} SET ${o} WHERE ${a}`;return(await M(L(r,!0),r.dbType,e)).executeQuery(n,{type:"UPDATE",allowBulkUpdate:!1})},ht=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}=Er(t.values),n=`INSERT INTO ${t.table} (${o}) VALUES (${a})`;return(await M(L(r,!0),r.dbType,e)).executeQuery(n,{type:"INSERT",allowBulkUpdate:!1})},Er=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}},wr=(e,t)=>{if(e.fn){if(oe(e.fn))return`${(t==="postgres"?Y:j)[e.fn](e)} as "${he(e)}"`;throw new Error("Function not allowed: "+e.fn)}return`${e.value} as "${e.value}"`},Rr=(e,t)=>{if(e.fn){if(oe(e.fn))return(t==="postgres"?Y:j)[e.fn]({...e,value:ft(e.value,t)});throw new Error("Function not allowed: "+e.fn)}return ft(e.value,t)},we=(e,t)=>t==="postgres"?`"${e}"`:t==="mysql"?`\`${e}\``:e,ft=(e,t)=>{let[r,o]=e.split(".");return we(r,t)+"."+we(o,t)},Sr=(e,t,r)=>{if(e&&e.length>0){let o=e.reduce((a,n)=>(a.set(he(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:we(a.column,r)}:a)}return t},br=(e,t,r)=>{let o=[];return t.length>0||r.length>0?o.push(...t,...r):e.length>0&&o.push(...e),o};var Et=e=>{},Cr=["--",";","DROP","drop"],wt=e=>{if(ae(e.value)&&e.value.startsWith("=")){let t=e.value;Cr.forEach(r=>{if(t.includes(r))throw new s(400,"Invalid input value for "+e.column)})}},Rt=e=>{if(!e.table)throw new s(400,"Table is required");e.values.forEach(wt)},St=e=>{if(!e.table)throw new s(400,"Table is required");e.values.forEach(wt)};var bt=w(e=>{e.route({method:"post",url:"/select",handler:async t=>{let r=y(t,Et);return{data:await Tt(t,r)}}}),e.route({method:"post",url:"/insert",config:{requireRole:C("editor")},handler:async t=>{let r=y(t,Rt);return{data:await ht(t,r)}}}),e.route({method:"post",url:"/update",config:{requireRole:C("editor")},handler:async t=>{let r=y(t,St);return{data:await gt(t,r)}}})});var Ct=w(e=>{e.get("/",{config:{isPublic:!0}},async()=>({data:{active:!0,version:l.str("SERVER_VERSION")}}))});var It=w(e=>{e.route({method:"get",url:"/:id/users",handler:async t=>{let{id:r}=R(t),o=await v.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:C("editor")},handler:async t=>S.transaction(async()=>{let r=t.user.id,o=y(t),a=b.create();a.id=r;let n=v.create(o);await v.save(n);let i=I.create({user:a,team:n});return await I.save(i),{data:n}})}),e.route({method:"patch",url:"/:id/user-role",config:{requireRole:C("admin")},handler:async t=>{let{id:r}=R(t),{role:o,userId:a}=y(t,({role:i})=>{if(i==="owner")throw new s(400,"Only one owner is allowed")});if((await I.findOneBy({user:{id:a},team:{id:r}}))?.role==="owner")throw new s(400,"Cannot change owner role");await I.update({user:{id:a},team:{id:r}},{role:o})}}),e.route({method:"delete",url:"/:id",config:{requireRole:C("admin")},handler:async t=>S.transaction(async()=>{let{id:r}=R(t),{userId:o}=B(t);if((await I.findOneBy({user:{id:o},team:{id:r}}))?.role==="owner")throw new s(400,"Cannot delete team owner");await b.update(o,{currentTeam:null}),await I.delete({user:{id:o},team:{id:r}}),await b.delete({id:o})})})});var Re=P(require("bcryptjs")),G=async e=>{let t=await Re.default.genSalt(10);return Re.default.hash(e,t)};var Nt=w(e=>{e.route({method:"get",url:"/",handler:async t=>{let r=await b.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=y(t);if(o.password&&(o.password=await G(o.password)),!(await b.update(r,o)).affected)throw new s(404,"User not found");let n=await b.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:"/",handler:async t=>S.transaction(async()=>{let r=y(t),o=await G(r.password),a=await b.save(b.create({username:r.username,password:o})),n=await I.save(I.create({role:"read_only",team:{id:r.teamId},user:{id:a.id}}));await b.update(a.id,{currentTeam:{id:n.id}})})})});var At=w(e=>{e.route({method:"get",url:"/",handler:async t=>{let r=t.user.id,o=await F.findOneBy({user:{id:r}});return o||(o=await F.save(F.create({user:{id:r}}))),{data:o}}}),e.route({method:"patch",url:"/",handler:async t=>{let{settings:r}=y(t);if(!r.id)throw new s(400,"Settings id is required!");if(!(await F.update(r.id,r)).affected)throw new s(404,"You do not own these settings!");return{data:await F.findOneBy({id:r.id})}}})});var Ot=w(e=>{e.route({method:"post",url:"/",config:{requireRole:C("editor")},handler:async t=>{let r=y(t),o=await x.save(x.create({isPersonal:!0,team:{id:t.user.currentTeamId},user:{id:t.user.id},query:{id:r.queryId}}));return await N.update(r.queryId,{name:r.name}),{data:o}}}),e.route({method:"delete",url:"/:id",config:{requireRole:C("editor")},handler:async t=>{let{id:r}=R(t);if(!(await x.delete({id:r})).affected)return{status:404,data:"Query not found"}}})});var Lt=require("node:path");var Ir=e=>e.routeOptions.config.isPublic?!0:!e.url.startsWith("/api/"),Pt=async e=>{if(Ir(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 Ye(o),n=await b.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 _t=(e,t)=>{e.__connections&&e.__connections.forEach(r=>{r.close()})};var qt=P(require("@fastify/cookie"));var Se={teamName:"Default Team",username:"admin",password:"admin"},Nr=async()=>{let e=await v.findOneBy({});return e||v.save(v.create({name:Se.teamName}))},Dt=async()=>{let e=await I.findOne({where:{role:"owner"},relations:{user:!0}});if(e)return e.user;let t=await Nr(),r=await G(Se.password),o=await b.save(b.create({username:Se.username,password:r})),a=await I.save(I.create({user:o,team:t,role:"owner"}));return await b.update(o.id,{currentTeam:a}),o};var A=(0,Ut.default)({querystringParser:e=>Mt.default.parse(e)}),Qt=l.num("PORT",4466),Ar=l.str("ALLOWED_ORIGINS","").split(",").map(e=>e.trim()),Or="0.0.0.0",Pr=[`http://localhost:${Qt}`,...Ar];function D(e,t){A.register(e,{prefix:t}),console.log("Registered "+t)}(async function(){Ae(),await A.register(qt.default,{}),await A.register(vt.default,{origin:(t,r)=>{!t||Pr.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(xt.default,{root:(0,Lt.join)(__dirname,"web")}),A.get("/",(t,r)=>{r.sendFile("index.html")}),A.addHook("onRequest",Pt),A.addHook("onRequest",st),A.addHook("onResponse",_t),D(Ke,"/api/auth"),D(it,"/api/data-sources"),D(ut,"/api/project"),D(ct,"/api/queries"),D(bt,"/api/runner"),D(Ct,"/api/status"),D(It,"/api/teams"),D(Nt,"/api/users"),D(At,"/api/user-settings"),D(Ot,"/api/saved-queries"),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 Fe(),await Dt(),A.listen({port:Qt,host:Or},(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),{})},Ze=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(c=>`'${c.tableID}-${c.columnID}'`),T=await cr(u,t);return{columns:a.map(c=>{let d=T[`${c.tableID}-${c.columnID}`];return{column:d?.column||c.name,alias:c.name,table:d?.table||"",full:d?d.table+"."+d.column:c.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)}},lr=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}},mr=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}},tt=async e=>{let t=await nr(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:()=>ur(e,t),executeQuery:(n,i)=>a(()=>i.type==="SELECT"?mr(t,()=>Ze(n,t,i)):lr(t,()=>Ze(n,t,i))),checkConnection:async()=>{},isClosed:()=>r,close:async()=>{if(!r)return r=!0,t.end()}}};var M=async(e,t,r)=>{try{let o;if(t==="mysql")o=await Xe(e);else if(t==="postgres")o=await tt(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 Z=P(require("node:crypto"));var rt="aes-256-gcm",dr=12,ot=()=>{let e=l.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},pr=e=>{let t=Z.default.randomBytes(dr),r=ot(),o=Z.default.createCipheriv(rt,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")}},fr=({encrypted:e,iv:t,tag:r})=>{let o=ot(),a=Z.default.createDecipheriv(rt,o,Buffer.from(t,"hex"));a.setAuthTag(Buffer.from(r,"hex"));let n=a.update(e,"hex","utf8");return n+=a.final("utf8"),n},ee={encrypt:pr,decrypt:fr};var L=(e,t=!1)=>{if(t){let r=ee.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 at=[{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"}],yr=at.reduce((e,t)=>(e[t.value]=t.label,e),{}),Ko=at.reduce((e,t)=>(e[t.label]=t.value,e),{}),k=e=>e.map(t=>({label:yr[t],value:t})),Vo=k(["=","<>",">",">=","<","<=","IN","NOT IN","IS NULL","IS NOT NULL"]),zo=k(["=","<>","LIKE","NOT LIKE","IN","NOT IN","IS NULL","IS NOT NULL"]),Jo=k(["=","<>","IS NULL","IS NOT NULL"]),Xo=k(["=","<>",">",">=","<","<=","IS NULL","IS NOT NULL"]),Zo=k(["IS NULL","IS NOT NULL"]),ea=k(["IN","NOT IN"]);var nt=["char","varchar","binary","varbinary","blob","text","enum","set","character","character varying","text","citext","uuid","xml","json","jsonb"];var ta=["date","datetime","timestamp","timestamptz"].reduce((e,t)=>(e[t]=!0,e),{});var he=e=>e.fn?e.distinct===!0?`${e.fn} distinct ${e.value}`:`${e.fn} ${e.value}`:e.value;var we={read_only:10,editor:20,admin:30,owner:40};var R=e=>{let t=we[e];return r=>we[r.currentTeamRole]>=t},st=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 it=E(e=>{e.route({method:"get",url:"/:id",handler:async t=>{let{id:r}=S(t),o=await w.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}=B(t);return{data:await w.find({where:{team:{id:r}},order:{createdAt:"DESC"}})}}}),e.route({url:"/",method:"post",config:{requireRole:R("admin")},handler:async t=>{let{teamId:r,ownerId:o,...a}=y(t,Ve),n=w.create({...a,allowUpdate:!!a.allowUpdate,allowInsert:!!a.allowInsert,team:{id:r},owner:{id:o}}),i=await M(L(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:T,encrypted:c}=ee.encrypt(n.dbPassword);return n.dbPassword=c,n.dbPasswordIv=T,n.dbPasswordTag=u,{data:await w.save(n)}}}),e.route({method:"put",url:"/:id",config:{requireRole:R("admin")},handler:async t=>{let{id:r}=S(t),o=y(t),a=await w.findOneBy({id:r});if(!a)throw new s(404,"Data source not found");let n=w.merge(a,o);return await w.save(n),{data:n}}}),e.route({method:"delete",url:"/:id",config:{requireRole:R("admin")},handler:async(t,r)=>b.transaction(async()=>{let{id:o}=S(t);await Promise.all([_.delete({datasource:{id:o}}),N.delete({dataSource:{id:o}})]),await w.delete({id:o})})}),e.route({method:"post",url:"/:id/inspect",handler:async(t,r)=>{let{id:o}=S(t),a=await w.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 w.save(a);let i=await(await M(L(a,!0),a.dbType,t)).inspectSchema();await _.delete({datasource:{id:o}}),await _.insert(i.sort().map(u=>_.create({tableName:u.tableName,columns:u.columns,datasource:{id:o}}))),a.status="READY",a.lastInspected=new Date,await w.save(a)}}),e.route({method:"get",url:"/:id/inspections",handler:async t=>{let{id:r}=S(t);return{data:await _.find({where:{datasource:{id:r}}})}}})});var $=require("typeorm"),ut=E(e=>{e.route({method:"get",url:"/team/:teamId/datasources",handler:async(t,r)=>{let{teamId:o}=S(t);return{data:await w.find({where:{team:{id:o}},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,r)=>{let a=S(t).teamId||t.user.currentTeamId;return{data:(await x.find({where:[{isPersonal:!1,team:{id:a}},{isPersonal:!0,team:{id:a},user:{id:t.user.id}}],relations:{query:!0},select:{id:!0,query:{id:!0,name:!0,updatedAt:!0}}})).map(u=>({name:u.query.name,id:u.query.id,updatedAt:u.query.updatedAt,savedQueryId:u.id}))}}}),e.route({method:"get",url:"/team/:teamId/query",handler:async t=>{let{teamId:r}=S(t),{search:o,size:a,selectedDataSources:n}=B(t),i=(parseInt(a)||20)/2,u={};n?.length&&(u.id=(0,$.In)(n));let[T,c]=await Promise.all([_.find({where:{tableName:(0,$.Like)(`%${o}%`),datasource:u},relations:{datasource:!0},select:{id:!0,tableName:!0,datasource:{name:!0,id:!0}},order:{tableName:"ASC"},take:i}),x.find({where:[{query:{dataSource:u,name:(0,$.Like)(`%${o}%`)},team:{id:r},isPersonal:!1},{query:{dataSource:u,name:(0,$.Like)(`%${o}%`)},team:{id:r},isPersonal:!0,user:{id:t.user.id}}],relations:{query:{dataSource:!0}},select:{id:!0,query:{id:!0,name:!0,dataSource:{name:!0}}},order:{query:{name:"ASC"}},take:i})]),d=[];return T.forEach(p=>{d.push({name:p.tableName,id:p.id,dataSourceName:p.datasource?.name||"--",dataSourceId:p.datasource?.id||"--",type:"table"})}),c.forEach(p=>{d.push({name:p.query.name,id:p.id,dataSourceName:p.query.dataSource?.name||"--",dataSourceId:p.query.dataSource?.id||"--",type:"query"})}),{data:d}}})});var ct=E(e=>{e.route({method:"get",url:"/:id",handler:async t=>{let{id:r}=S(t),o=await N.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:R("editor")},handler:async t=>{let r=y(t),o=await w.findOne({where:{id:r.dataSourceId},relations:{team:!0}});return{data:await N.save(N.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:R("editor")},handler:async t=>{let{id:r}=S(t),o=y(t);if(!(await N.update(r,o)).affected)throw new s(404,"Query not found");return{data:await N.findOneBy({id:r})}}}),e.route({method:"delete",url:"/:id",config:{requireRole:R("editor")},handler:async t=>b.transaction(async()=>{let{id:r}=S(t);if(!(await N.delete({id:r})).affected)return{status:404,data:"Query not found"}})})});var te=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:te,MAX:te,MIN:te,COUNT:te};var re=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:re,MAX:re,MIN:re,COUNT:re};var lt=["SUM","COUNT","AVG","MAX","MIN"],Tr=["YEAR","MONTH","DAY",...lt],gr=Tr.reduce((e,t)=>(e[t]=!0,e),{}),hr=lt.reduce((e,t)=>(e[t]=!0,e),{}),oe=e=>gr[e],mt=e=>hr[e],dt=(e,t)=>e.fn&&oe(e.fn)?(t==="postgres"?Y:j)[e.fn](e):e.value;var ae=e=>typeof e=="string",pt=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},W=(e,t)=>{let{column:r,operator:o,value:a,fn:n}=e,i=dt({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(m=>ae(m.value)?`'${m.value}'`:m.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 d=a?.[0],p;return ae(d?.value)&&d?.isColumn!==!0?p=`'${d?.value}'`:p=d?.value,`${i} ${o} ${p}`}};var ne=class{constructor(t="mysql"){this.dialect=t,this.skeleton={type:"SELECT"}}addWhere(t){let r=W(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=W(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 pt(this.skeleton)}};var yt=require("typeorm");var Tt=async(e,t)=>{let{datasourceId:r,size:o,page:a,name:n}=t,{table:i,filters:u,joins:T,groupBy:c,searchAll:d,orderBy:p}=t.opts,m=br(t.opts.columns,t.opts.groupBy,t.opts.aggregations),g=await w.findOne({where:{id:r},select:["id","dbType","dbDatabase","dbPassword","dbPasswordTag","dbPasswordIv","dbPort","dbUrl","dbSchema","dbUser"]}),Q=[i],K=[];if(!g)throw new s(404,"Data source not found");let Ft=await N.save(N.create({user:{id:e.user.id},team:{id:e.user.currentTeamId},dataSource:{id:r},name:n,opts:t.opts})),O=new ne(g.dbType);O.setTable(i),O.setLimit(o||20),O.setOffset(o*a),u?.forEach(h=>{h.fn&&mt(h.fn)?O.addHaving(h):O.addWhere(h)}),T&&(O.addJoin(...T),T.forEach(h=>{Q.push(h.table)}));let be=Rr(m,p,g.dbType);be.length>0&&O.addOrderBy(...be),c&&c.length>0&&c.forEach(h=>O.addGroupBy(Sr(h,g.dbType)));let Bt=await _.find({where:{tableName:(0,yt.In)(Q),datasource:{id:r}}});for(let h of Bt)if(h.columns)for(let U of h.columns)K.push({column:U.name,table:h.tableName||"",full:`${h.tableName}.${U.name}`,type:U.type});let V;if(m&&m.length>0?V=m.map(h=>Er(h,g.dbType)):V=K.map(h=>`${h.full} as "${h.full}"`),O.selectColumns(V),d){let h=K.filter(U=>nt.includes(U.type)&&V.some(se=>se.startsWith(U.full)));if(h.length>0){let U=h.map(se=>`LOWER(${se.full}) LIKE '%${d.toLowerCase()}%'`);O.addWhereRaw(`(${U.join(" OR ")})`,"AND")}}return{...await(await M(L(g,!0),g.dbType,e)).executeQuery(O.toSQL(),{type:"SELECT",allowBulkUpdate:!1}),queryHistoryId:Ft.id,tables:Q,allColumns:K}},gt=async(e,t)=>{let r=await w.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:T})=>typeof u=="string"?u&&u.startsWith("=")?`${T}=${u.substring(1)}`:`${T}='${u}'`:`${T}='${u}'`).join(", "),a=t.filters.map(u=>W(u,r.dbType)).join(" AND "),n=`UPDATE ${t.table} SET ${o} WHERE ${a}`;return(await M(L(r,!0),r.dbType,e)).executeQuery(n,{type:"UPDATE",allowBulkUpdate:!1})},ht=async(e,t)=>{let r=await w.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}=wr(t.values),n=`INSERT INTO ${t.table} (${o}) VALUES (${a})`;return(await M(L(r,!0),r.dbType,e)).executeQuery(n,{type:"INSERT",allowBulkUpdate:!1})},wr=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}},Er=(e,t)=>{if(e.fn){if(oe(e.fn))return`${(t==="postgres"?Y:j)[e.fn](e)} as "${he(e)}"`;throw new Error("Function not allowed: "+e.fn)}return`${e.value} as "${e.value}"`},Sr=(e,t)=>{if(e.fn){if(oe(e.fn))return(t==="postgres"?Y:j)[e.fn]({...e,value:ft(e.value,t)});throw new Error("Function not allowed: "+e.fn)}return ft(e.value,t)},Ee=(e,t)=>t==="postgres"?`"${e}"`:t==="mysql"?`\`${e}\``:e,ft=(e,t)=>{let[r,o]=e.split(".");return Ee(r,t)+"."+Ee(o,t)},Rr=(e,t,r)=>{if(e&&e.length>0){let o=e.reduce((a,n)=>(a.set(he(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:Ee(a.column,r)}:a)}return t},br=(e,t,r)=>{let o=[];return t.length>0||r.length>0?o.push(...t,...r):e.length>0&&o.push(...e),o};var wt=e=>{},Ir=["--",";","DROP","drop"],Et=e=>{if(ae(e.value)&&e.value.startsWith("=")){let t=e.value;Ir.forEach(r=>{if(t.includes(r))throw new s(400,"Invalid input value for "+e.column)})}},St=e=>{if(!e.table)throw new s(400,"Table is required");e.values.forEach(Et)},Rt=e=>{if(!e.table)throw new s(400,"Table is required");e.values.forEach(Et)};var bt=E(e=>{e.route({method:"post",url:"/select",handler:async t=>{let r=y(t,wt);return{data:await Tt(t,r)}}}),e.route({method:"post",url:"/insert",config:{requireRole:R("editor")},handler:async t=>{let r=y(t,St);return{data:await ht(t,r)}}}),e.route({method:"post",url:"/update",config:{requireRole:R("editor")},handler:async t=>{let r=y(t,Rt);return{data:await gt(t,r)}}})});var It=E(e=>{e.get("/",{config:{isPublic:!0}},async()=>({data:{active:!0,version:l.str("SERVER_VERSION")}}))});var Ct=E(e=>{e.route({method:"get",url:"/:id/users",handler:async t=>{let{id:r}=S(t),o=await v.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:R("editor")},handler:async t=>b.transaction(async()=>{let r=t.user.id,o=y(t),a=I.create();a.id=r;let n=v.create(o);await v.save(n);let i=C.create({user:a,team:n});return await C.save(i),{data:n}})}),e.route({method:"patch",url:"/:id/user-role",config:{requireRole:R("admin")},handler:async t=>{let{id:r}=S(t),{role:o,userId:a}=y(t,({role:i})=>{if(i==="owner")throw new s(400,"Only one owner is allowed")});if((await C.findOneBy({user:{id:a},team:{id:r}}))?.role==="owner")throw new s(400,"Cannot change owner role");await C.update({user:{id:a},team:{id:r}},{role:o})}}),e.route({method:"delete",url:"/:id",config:{requireRole:R("admin")},handler:async t=>b.transaction(async()=>{let{id:r}=S(t),{userId:o}=B(t);if((await C.findOneBy({user:{id:o},team:{id:r}}))?.role==="owner")throw new s(400,"Cannot delete team owner");await I.update(o,{currentTeam:null}),await C.delete({user:{id:o},team:{id:r}}),await I.delete({id:o})})})});var Se=P(require("bcryptjs")),G=async e=>{let t=await Se.default.genSalt(10);return Se.default.hash(e,t)};var Nt=E(e=>{e.route({method:"get",url:"/",handler:async t=>{let r=await I.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=y(t);if(o.password&&(o.password=await G(o.password)),!(await I.update(r,o)).affected)throw new s(404,"User not found");let n=await I.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:R("admin")},handler:async t=>b.transaction(async()=>{let r=y(t),o=await G(r.password),a=await I.save(I.create({username:r.username,password:o})),n=await C.save(C.create({role:"read_only",team:{id:r.teamId},user:{id:a.id}}));await I.update(a.id,{currentTeam:{id:n.id}})})})});var At=E(e=>{e.route({method:"get",url:"/",handler:async t=>{let r=t.user.id,o=await F.findOneBy({user:{id:r}});return o||(o=await F.save(F.create({user:{id:r}}))),{data:o}}}),e.route({method:"patch",url:"/",handler:async t=>{let{settings:r}=y(t);if(!r.id)throw new s(400,"Settings id is required!");if(!(await F.update(r.id,r)).affected)throw new s(404,"You do not own these settings!");return{data:await F.findOneBy({id:r.id})}}})});var Ot=E(e=>{e.route({method:"post",url:"/",config:{requireRole:R("editor")},handler:async t=>{let r=y(t),o=await x.save(x.create({isPersonal:!0,team:{id:t.user.currentTeamId},user:{id:t.user.id},query:{id:r.queryId}}));return await N.update(r.queryId,{name:r.name}),{data:o}}}),e.route({method:"delete",url:"/:id",config:{requireRole:R("editor")},handler:async t=>{let{id:r}=S(t);if(!(await x.delete({id:r})).affected)return{status:404,data:"Query not found"}}})});var Lt=require("node:path");var Cr=e=>e.routeOptions.config.isPublic?!0:!e.url.startsWith("/api/"),Pt=async e=>{if(Cr(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 Ye(o),n=await I.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 _t=(e,t)=>{e.__connections&&e.__connections.forEach(r=>{r.close()})};var qt=P(require("@fastify/cookie"));var Re={teamName:"Default Team",username:"admin",password:"admin"},Nr=async()=>{let e=await v.findOneBy({});return e||v.save(v.create({name:Re.teamName}))},Dt=async()=>{let e=await C.findOne({where:{role:"owner"},relations:{user:!0}});if(e)return e.user;let t=await Nr(),r=await G(Re.password),o=await I.save(I.create({username:Re.username,password:r})),a=await C.save(C.create({user:o,team:t,role:"owner"}));return await I.update(o.id,{currentTeam:a}),o};var A=(0,Ut.default)({querystringParser:e=>Mt.default.parse(e)}),Qt=l.num("PORT",4466),Ar=l.str("ALLOWED_ORIGINS","").split(",").map(e=>e.trim()),Or="0.0.0.0",Pr=[`http://localhost:${Qt}`,...Ar];function D(e,t){A.register(e,{prefix:t}),console.log("Registered "+t)}(async function(){Ae(),await A.register(qt.default,{}),await A.register(vt.default,{origin:(t,r)=>{!t||Pr.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(xt.default,{root:(0,Lt.join)(__dirname,"web")}),A.get("/",(t,r)=>{r.sendFile("index.html")}),A.addHook("onRequest",Pt),A.addHook("onRequest",st),A.addHook("onResponse",_t),D(Ke,"/api/auth"),D(it,"/api/data-sources"),D(ut,"/api/project"),D(ct,"/api/queries"),D(bt,"/api/runner"),D(It,"/api/status"),D(Ct,"/api/teams"),D(Nt,"/api/users"),D(At,"/api/user-settings"),D(Ot,"/api/saved-queries"),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 Fe(),await Dt(),A.listen({port:Qt,host:Or},(t,r)=>{t&&(console.error(t),process.exit(1)),console.log(`Server listening at ${r}`)})})();
|