├── assets ├── phlappjack-dependency.png └── logo.svg ├── leaf.ts ├── AppConfiguration ├── Imports │ ├── ImportsForRouter.ts │ ├── ImportsForServer.ts │ └── ImportsForMongo.ts ├── CRUDFunctions.ts └── appConfigurator.ts ├── client ├── index.tsx └── components │ ├── Oak │ ├── ExistingEndPoints │ │ ├── EndPointNameInput.tsx │ │ ├── ExistingEndPoints.tsx │ │ └── ExistingEndpointsDisplay.tsx │ ├── RoutingInformation │ │ ├── RouteExecutionOrder.tsx │ │ ├── ResponseStatus.tsx │ │ ├── RoutingMethods.tsx │ │ ├── RoutingInformation.tsx │ │ ├── Middleware.tsx │ │ └── RouteExcecutionOrderDisplay.tsx │ └── Oak.tsx │ ├── MongoDB │ ├── DBInput │ │ ├── DBInputFieldKey.tsx │ │ ├── DBInputFieldValue.tsx │ │ ├── DBInputFieldDisplay.tsx │ │ └── DBInputField.tsx │ ├── MongoAtlasConnection │ │ ├── MongoDBInput.tsx │ │ ├── MongoDBDisplay.tsx │ │ ├── MongoHostInput.tsx │ │ ├── MongoHostDisplay.tsx │ │ ├── MongoPasswordInput.tsx │ │ ├── MongoPasswordDisplay.tsx │ │ ├── MongoUserNameDisplay.tsx │ │ ├── MongoUserNameInput.tsx │ │ └── MongoAtlasConnection.tsx │ ├── ExistingModels │ │ ├── ExistingModels.tsx │ │ ├── DBNameInput.tsx │ │ └── ExistingModelsDisplay.tsx │ └── MongoDB.tsx │ ├── Docker │ ├── _DockerFileInterface.tsx │ ├── DockerComposeInterface.tsx │ ├── DockerCompose.tsx │ ├── _DockerFile.tsx │ └── Docker.tsx │ ├── Footer.tsx │ ├── Home │ ├── CreateNewApplicationInput.tsx │ ├── LoadExistingApplication.tsx │ ├── CreateNewApplication.tsx │ ├── Home.tsx │ └── LoadExistingApplicationsDisplay.tsx │ ├── Header.tsx │ ├── Deno │ └── Deno.tsx │ ├── MyDrawer.tsx │ └── App.tsx ├── scripts.json ├── createBundle.ts ├── deps.ts ├── docker-compose.yml ├── index.html ├── server ├── routes.ts ├── models │ └── userModel.ts ├── server.ts~a0ec3fdcac635b29b71614a97c3c06ef7546dd04 └── controllers │ └── controllers.ts ├── tsconfig.json ├── gitfunctions ├── gitClone.ts └── gitCommitPush.ts ├── Dockerfile ├── LICENSE ├── templates ├── envConstructor.ts ├── constructor.ts └── schemaBuilder.ts ├── webpack.config.js ├── .gitignore ├── openChrome.ts ├── package.json ├── mod.ts └── README.md /assets/phlappjack-dependency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/phlappjack/HEAD/assets/phlappjack-dependency.png -------------------------------------------------------------------------------- /leaf.ts: -------------------------------------------------------------------------------- 1 | import { Leaf } from "./deps.ts"; 2 | 3 | Leaf.compile({ 4 | modulePath: "./mod.ts", 5 | contentFolders: ["./build"], 6 | flags: ["--allow-all", "--unstable"] 7 | }) -------------------------------------------------------------------------------- /AppConfiguration/Imports/ImportsForRouter.ts: -------------------------------------------------------------------------------- 1 | export const routerString: string = 'import { Router } from "https://deno.land/x/oak/mod.ts";' 2 | export const exportString: string = 'export default router' 3 | -------------------------------------------------------------------------------- /client/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { render } from 'react-dom'; 3 | import App from './components/App'; 4 | 5 | render( 6 | , 7 | document.getElementById("root") as HTMLElement, 8 | ); -------------------------------------------------------------------------------- /scripts.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://deno.land/x/denon@2.4.7/schema.json", 3 | "scripts": { 4 | "start": { 5 | "cmd": "deno run --allow-net server/server.ts", 6 | "desc": "run my server.ts file" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /createBundle.ts: -------------------------------------------------------------------------------- 1 | const createBundle = async () => { 2 | const { files, diagnostics } = await Deno.emit("./client/index.tsx", { 3 | bundle: "module", 4 | }); 5 | 6 | await Deno.writeTextFile("./build/bundle.js", files["deno:///bundle.js"]); 7 | }; 8 | 9 | export { createBundle }; 10 | -------------------------------------------------------------------------------- /deps.ts: -------------------------------------------------------------------------------- 1 | export { Application, send, Router } from "https://deno.land/x/oak/mod.ts"; 2 | export { Bson, MongoClient } from "https://deno.land/x/mongo/mod.ts"; 3 | export { config } from "https://deno.land/x/dotenv/mod.ts"; 4 | export * from "https://deno.land/x/dotenv/load.ts"; 5 | export { Leaf } from "https://nonstdout.github.io/leaf/mod.ts"; -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | 3 | services: 4 | mongo: 5 | image: mongo 6 | restart: always 7 | ports: 8 | - 27018:27017 9 | environment: 10 | MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER} 11 | MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASS} 12 | 13 | phlappjack: 14 | image: phlappjack 15 | ports: 16 | - 8000:8000 17 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Phlappjack 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /server/routes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "https://deno.land/x/oak/mod.ts"; 2 | 3 | const router = new Router(); // Create Router 4 | 5 | router 6 | .get("/api/quote", getQuotes) // Get all quotes 7 | .get("/api/quote/:id", getQuote) // Get one quote of quoteID: id 8 | .post("/api/quote", addQuote) // Add a quote 9 | .put("/api/quote/:id", updateQuote) // Update a quote 10 | .delete("/api/quote/:id", deleteQuote); // Delete a quote 11 | 12 | export default router; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "compilerOptions": { 4 | "lib": ["es6", "dom"], 5 | "jsx": "react", 6 | "module": "commonjs", 7 | "noImplicitAny": true, 8 | "noImplicitThis": true, 9 | "strictNullChecks": true, 10 | "outDir": "./build/", 11 | "preserveConstEnums": true, 12 | "removeComments": true, 13 | "sourceMap": true, 14 | "target": "es5" 15 | }, 16 | "include": [ 17 | "src/components/index.tsx" 18 | ] 19 | } -------------------------------------------------------------------------------- /client/components/Oak/ExistingEndPoints/EndPointNameInput.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import TextField from '@material-ui/core/TextField'; 4 | 5 | export default function EndPointNameInput(){ 6 | 7 | return ( 8 |
9 |
10 | 17 | 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /client/components/MongoDB/DBInput/DBInputFieldKey.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import TextField from '@material-ui/core/TextField'; 4 | 5 | export default function DBInputFieldKey(props){ 6 | 7 | const handleClick = (e) =>{ 8 | if(e.keyCode === 13){ 9 | e.preventDefault(); 10 | } 11 | } 12 | 13 | return ( 14 |
15 |
16 | 23 | 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /AppConfiguration/Imports/ImportsForServer.ts: -------------------------------------------------------------------------------- 1 | 2 | export const importString: string = `import { Application } from "./deps.ts"; 3 | import { router } from "./Routes/Router.ts"; 4 | import { oakCors } from "./deps.ts"; 5 | ` 6 | 7 | export const setUp: string = ` 8 | 9 | const app = new Application(); 10 | const port: number = 8000; 11 | 12 | app.use( 13 | oakCors({ 14 | origin: "*" 15 | }), 16 | ); 17 | 18 | app.use(router.routes()); 19 | app.use(router.allowedMethods()); 20 | 21 | ` 22 | 23 | 24 | export const fetchHandler: string = ` 25 | // app.addEventListener("fetch", app.fetchEventHandler()) 26 | console.log('running on port ', port); 27 | await app.listen({ port: port }); 28 | 29 | ` -------------------------------------------------------------------------------- /AppConfiguration/Imports/ImportsForMongo.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | export const mongooseString = (mongoHostState ,userNameState ,passWordState ,mongoDBState) => { 5 | 6 | return `import { MongoClient } from '../deps.ts' 7 | const client = new MongoClient(); 8 | 9 | await client.connect({ 10 | tls: true, 11 | db:'', 12 | servers: [ 13 | { 14 | host: "${mongoHostState}", 15 | port: 27017, 16 | }, 17 | ], 18 | credential: { 19 | username: "${userNameState}", 20 | password: "${passWordState}", 21 | db: "${mongoDBState}", 22 | mechanism: "SCRAM-SHA-1", 23 | }, 24 | }); 25 | 26 | export {client}; 27 | ` 28 | 29 | } -------------------------------------------------------------------------------- /gitfunctions/gitClone.ts: -------------------------------------------------------------------------------- 1 | export default async function gitClone(dir, repoUrl) { 2 | 3 | const userGithubRepo = repoUrl; 4 | const cmd = Deno.run({ 5 | cmd: ["git", "clone", userGithubRepo, Deno.cwd() + dir], 6 | stdout: "piped", 7 | stderr: "piped", 8 | 9 | }) 10 | 11 | const { code } = await cmd.status(); // (*1); wait here for child to finish 12 | 13 | const rawOutput = await cmd.output(); 14 | const rawError = await cmd.stderrOutput(); 15 | 16 | 17 | if (code === 0) { 18 | const output = await Deno.stdout.write(rawOutput); 19 | console.log(`Cloned ${userGithubRepo} sucessfully into ${Deno.cwd()}${dir}`) 20 | } else { 21 | const errorString = new TextDecoder().decode(rawError); 22 | console.log(errorString); 23 | } 24 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM hayd/alpine-deno:1.10.2 2 | 3 | # The port that your application listens to. 4 | EXPOSE 3000 5 | 6 | WORKDIR /phlappjack 7 | 8 | # Prefer not to run as root. 9 | # USER deno 10 | 11 | # Cache the dependencies as a layer (the following two steps are re-run only when deps.ts is modified). 12 | # Ideally cache deps.ts will download and compile _all_ external files used in main.ts. 13 | COPY . . 14 | RUN deno cache --unstable deps.ts 15 | RUN deno cache ./client/deps.ts 16 | # RUN deno bundle ./client/index.tsx ./build/bundle.js 17 | 18 | 19 | # These steps will be re-run upon each file change in your working directory: 20 | ADD . . 21 | # Compile the main app so that it doesn't need to be compiled each startup/entry. 22 | # RUN deno cache mod.ts 23 | 24 | CMD ["deno", "run", "-A", "--unstable", "mod.ts"] 25 | -------------------------------------------------------------------------------- /client/components/Docker/_DockerFileInterface.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import Paper from '@material-ui/core/Paper'; 5 | import AddCircleIcon from '@material-ui/icons/AddCircle'; 6 | import TextField from '@material-ui/core/TextField'; 7 | 8 | 9 | export default function _DockerFileInterface(props){ 10 | 11 | const handleChange = (e) => { 12 | props.setDockerFile(e.target.value as HTMLFontElement); 13 | } 14 | 15 | return ( 16 |
17 | {/* */} 18 | 25 | {/* */} 26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /client/components/Footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createStyles, Grid, Button, Typography} from '@material-ui/core'; 3 | import { makeStyles, Theme } from '@material-ui/core/styles'; 4 | import SaveIcon from '@material-ui/icons/Save'; 5 | 6 | const useStyles = makeStyles((theme:Theme) => 7 | createStyles({ 8 | button: { 9 | margin: theme.spacing(1), 10 | }, 11 | }), 12 | ); 13 | 14 | export default function Footer(){ 15 | const classes = useStyles(); 16 | return ( 17 | 18 | 19 |
20 |
21 | 22 | © 2021 OSLabs, Phlappjack Inc 23 | 24 |
25 |
26 | ) 27 | } -------------------------------------------------------------------------------- /server/models/userModel.ts: -------------------------------------------------------------------------------- 1 | import { Bson, MongoClient } from "../../deps.ts"; 2 | 3 | // set vars from .env file 4 | const dbName = Deno.env.get("MONGO_DB"); 5 | const dbUser = Deno.env.get("MONGO_USER"); 6 | const dbPass = Deno.env.get("MONGO_PASS"); 7 | const dbHostname = Deno.env.get("MONGO_HOST"); 8 | 9 | const client = new MongoClient(); 10 | 11 | const url = 12 | `mongodb+srv://${dbUser}:${dbPass}@${dbHostname}/${dbName}?authMechanism=SCRAM-SHA-1&retryWrites=true&w=majority`; 13 | await client.connect(url); 14 | 15 | // Defining schema interface 16 | interface UserSchema { 17 | username: string; 18 | password: string; 19 | } 20 | 21 | const db = client.database("test"); 22 | const users = db.collection("users"); 23 | 24 | await users.insertOne({ 25 | username: "user1", 26 | password: "pass1", 27 | }); 28 | 29 | const dbs = await client.listDatabases(); 30 | 31 | console.log(dbs); 32 | -------------------------------------------------------------------------------- /client/components/MongoDB/MongoAtlasConnection/MongoDBInput.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import TextField from '@material-ui/core/TextField'; 3 | 4 | 5 | export default function MongoDBInput(props){ 6 | 7 | const handleKeyPress = (e) => { 8 | if(e.keyCode === 13){ 9 | e.preventDefault(); 10 | const mongoDBInputEle = e.target as HTMLElement; 11 | props.setAtlasDB(mongoDBInputEle.value) 12 | mongoDBInputEle.value = ''; 13 | } 14 | } 15 | 16 | return ( 17 |
18 |
19 | handleKeyPress(e)} 26 | /> 27 | 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /client/components/MongoDB/MongoAtlasConnection/MongoDBDisplay.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStyles, Divider, Paper, List,ListItem, ListItemText, IconButton} from '@material-ui/core'; 3 | import { makeStyles, Theme } from '@material-ui/core/styles'; 4 | import Fab from '@material-ui/core/Fab'; 5 | 6 | export default function MongoDBDisplay(props){ 7 | 8 | const onRender = () =>{ 9 | if(props.atlasDB.length) { 10 | return ('Database: ' + props.atlasDB); 11 | } 12 | else return; 13 | } 14 | return ( 15 |
16 | 25 | {onRender()} 26 | 27 |
28 | ) 29 | } -------------------------------------------------------------------------------- /client/components/MongoDB/MongoAtlasConnection/MongoHostInput.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import TextField from '@material-ui/core/TextField'; 3 | 4 | 5 | export default function MongoHostInput(props){ 6 | 7 | const handleKeyPress = (e) => { 8 | if(e.keyCode === 13){ 9 | e.preventDefault(); 10 | const mongoHostInputEle = e.target as HTMLElement; 11 | props.setAtlasHostCluster(mongoHostInputEle.value) 12 | mongoHostInputEle.value = ''; 13 | } 14 | } 15 | 16 | return ( 17 |
18 |
19 | handleKeyPress(e)} 26 | /> 27 | 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /client/components/MongoDB/MongoAtlasConnection/MongoHostDisplay.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStyles, Divider, Paper, List,ListItem, ListItemText, IconButton} from '@material-ui/core'; 3 | import { makeStyles, Theme } from '@material-ui/core/styles'; 4 | import Fab from '@material-ui/core/Fab'; 5 | 6 | export default function MongoHostDisplay(props){ 7 | 8 | const onRender = () =>{ 9 | if(props.atlasHostCluster.length) { 10 | return ('Host: ' + props.atlasHostCluster); 11 | } 12 | else return; 13 | } 14 | return ( 15 |
16 | 25 | {onRender()} 26 | 27 |
28 | ) 29 | } -------------------------------------------------------------------------------- /client/components/MongoDB/MongoAtlasConnection/MongoPasswordInput.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import TextField from '@material-ui/core/TextField'; 3 | 4 | 5 | export default function MongoPasswordInput(props){ 6 | 7 | const handleKeyPress = (e) => { 8 | if(e.keyCode === 13){ 9 | e.preventDefault(); 10 | const mongoPasswordInputEle = e.target as HTMLElement; 11 | props.setAtlasPassword(mongoPasswordInputEle.value) 12 | mongoPasswordInputEle.value = ''; 13 | } 14 | } 15 | 16 | return ( 17 |
18 |
19 | handleKeyPress(e)} 26 | /> 27 | 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /client/components/MongoDB/MongoAtlasConnection/MongoPasswordDisplay.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStyles, Divider, Paper, List,ListItem, ListItemText, IconButton} from '@material-ui/core'; 3 | import { makeStyles, Theme } from '@material-ui/core/styles'; 4 | import Fab from '@material-ui/core/Fab'; 5 | 6 | export default function MongoPasswordDisplay(props){ 7 | 8 | const onRender = () =>{ 9 | if(props.atlasPassword.length) { 10 | return ('Password: ' + props.atlasPassword); 11 | } 12 | else return; 13 | } 14 | return ( 15 |
16 | 25 | {onRender()} 26 | 27 |
28 | ) 29 | -------------------------------------------------------------------------------- /client/components/MongoDB/MongoAtlasConnection/MongoUserNameDisplay.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStyles, Divider, Paper, List,ListItem, ListItemText, IconButton} from '@material-ui/core'; 3 | import { makeStyles, Theme } from '@material-ui/core/styles'; 4 | import Fab from '@material-ui/core/Fab'; 5 | 6 | export default function MongoUserNameDisplay(props){ 7 | 8 | const onRender = () =>{ 9 | if(props.atlasUserName.length) { 10 | return ('Username: ' + props.atlasUserName); 11 | } 12 | else return; 13 | } 14 | return ( 15 |
16 | 25 | {onRender()} 26 | 27 |
28 | ) 29 | } -------------------------------------------------------------------------------- /client/components/Docker/DockerComposeInterface.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import Paper from '@material-ui/core/Paper'; 5 | import AddCircleIcon from '@material-ui/icons/AddCircle'; 6 | import TextField from '@material-ui/core/TextField'; 7 | import Autocomplete from '@material-ui/core/Autocomplete'; 8 | 9 | 10 | export default function DockerComposeInterface(props){ 11 | 12 | const handleChange = (e) => { 13 | props.setDockerCompose(e.target.value as HTMLFontElement); 14 | } 15 | 16 | return ( 17 |
18 | {/* */} 19 | 27 | {/* */} 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /client/components/Home/CreateNewApplicationInput.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import TextField from '@material-ui/core/TextField'; 3 | 4 | export default function CreateNewApplicationInput(props){ 5 | 6 | const handleKeyPress = (e) => { 7 | if(e.keyCode === 13){ 8 | e.preventDefault(); 9 | const createNewAppEle = e.target as HTMLElement; 10 | props.setNewApplication(createNewAppEle.value); 11 | createNewAppEle.value = ''; 12 | //Force parent component to update. 13 | const newChildKey: number = Math.floor(Math.random() * 100000); 14 | props.setChildKey(newChildKey); 15 | } 16 | } 17 | 18 | return ( 19 |
20 | handleKeyPress(e)} 27 | /> 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /client/components/MongoDB/MongoAtlasConnection/MongoUserNameInput.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import TextField from '@material-ui/core/TextField'; 3 | 4 | 5 | export default function MongoUserNameInput(props){ 6 | 7 | const handleKeyPress = (e) => { 8 | if(e.keyCode === 13){ 9 | e.preventDefault(); 10 | const mongoUserNameInputEle = e.target as HTMLElement; 11 | props.setAtlasUserName(mongoUserNameInputEle.value) 12 | mongoUserNameInputEle.value = ''; 13 | } 14 | } 15 | 16 | return ( 17 |
18 |
19 | handleKeyPress(e)} 26 | onSubmit = {(e) => handleSubmit(e)} 27 | /> 28 | 29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /gitfunctions/gitCommitPush.ts: -------------------------------------------------------------------------------- 1 | export default async function gitCommitPush(dir, repoUrl) { 2 | 3 | const userGithubRepo = repoUrl; 4 | 5 | Deno.chdir(dir) 6 | 7 | const commands = [ 8 | { 9 | cmd: ["git", "add", "."], 10 | stdout: "piped", 11 | stderr: "piped", 12 | }, 13 | { 14 | cmd: ["git", "commit", "-am", "'DENO COMMIT'"], 15 | stdout: "piped", 16 | stderr: "piped", 17 | }, 18 | { 19 | cmd: ["git", "push"], 20 | stdout: "piped", 21 | stderr: "piped", 22 | }, 23 | ] 24 | 25 | for await (const item of commands) { 26 | 27 | let cmd = Deno.run(item) 28 | 29 | 30 | const { code } = await cmd.status(); // (*1); wait here for child to finish 31 | 32 | const rawOutput = await cmd.output(); 33 | const rawError = await cmd.stderrOutput(); 34 | 35 | 36 | if (code === 0) { 37 | await Deno.stdout.write(rawOutput); 38 | console.log(`Commited ${userGithubRepo} sucessfully.`) 39 | } else { 40 | const errorString = new TextDecoder().decode(rawError); 41 | console.log(errorString); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 OS Labs Beta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /server/server.ts~a0ec3fdcac635b29b71614a97c3c06ef7546dd04: -------------------------------------------------------------------------------- 1 | import { Oak, path } from "./deps.ts"; 2 | 3 | const HOSTNAME = "0.0.0.0"; 4 | const PORT = 3000; 5 | 6 | const app = new Oak.Application(); 7 | 8 | // Don't use Deno.cwd(), since that requires access to the root of the directory, and the application 9 | // only really needs read access to the public folder 10 | 11 | // Send static content 12 | app.use(async (ctx) => { 13 | await Oak.send(ctx, ctx.request.url.pathname, { 14 | hidden: true, 15 | root: path.fromFileUrl(new URL("../client", import.meta.url)), 16 | index: "index.html", 17 | }).catch(async (e) => { 18 | // If the file is not found, redirect to the React app where a 404 page can be displayed if desired 19 | if (e instanceof Oak.httpErrors.NotFound) { 20 | // This was made manually so that I didn't have to use the send methods again 21 | const imageBuf = await Deno.readFile( 22 | new URL("../client/index.html", import.meta.url), 23 | ); 24 | ctx.response.body = imageBuf; 25 | ctx.response.headers.set("Content-Type", "text/html; charset=utf-8"); 26 | } else { 27 | throw e; 28 | } 29 | }); 30 | }); 31 | 32 | console.log(`Server listening on http://${HOSTNAME}/:${PORT}`); 33 | await app.listen({ 34 | hostname: HOSTNAME, 35 | port: PORT, 36 | }); -------------------------------------------------------------------------------- /templates/envConstructor.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "https://deno.land/std@0.83.0/fs/mod.ts"; 2 | import { 3 | prettier, 4 | prettierPlugins 5 | } from "https://denolib.com/denolib/prettier/prettier.ts"; 6 | 7 | interface envPKG { 8 | MONGO_HOST: string; 9 | MONGO_DB: string; 10 | MONGO_USER: string; 11 | MONGO_PASS: string; 12 | } 13 | 14 | const testPKG: envPKG = { 15 | MONGO_HOST: "cluster0.yybae.mongodb.net", 16 | MONGO_DB: "phlappjack", 17 | MONGO_USER: "dbUser", 18 | MONGO_PASS: "secret123", 19 | } 20 | 21 | 22 | export const envConstructor = async (model: envPKG) => { 23 | 24 | 25 | let envCluster = '' 26 | for (const [k, v] of Object.entries(model)){ 27 | envCluster += `${k}=${v}\n` 28 | } 29 | 30 | await fs.ensureFile('./.env') 31 | Deno.writeTextFile('./.env', envCluster) 32 | 33 | //return model 34 | } 35 | 36 | console.log(envConstructor(testPKG)) 37 | 38 | // const dbName = Deno.env.get("MONGO_DB"); 39 | // const dbUser = Deno.env.get("MONGO_USER"); 40 | // const dbPass = Deno.env.get("MONGO_PASS"); 41 | // const dbHostname = Deno.env.get("MONGO_HOST"); 42 | 43 | // const client = new MongoClient(); 44 | 45 | // const url = 46 | // `mongodb+srv://${dbUser}:${dbPass}@${dbHostname}/${dbName}?authMechanism=SCRAM-SHA-1&retryWrites=true&w=majority`; 47 | // await client.connect(url); -------------------------------------------------------------------------------- /client/components/Docker/DockerCompose.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import Paper from '@material-ui/core/Paper'; 5 | import AddCircleIcon from '@material-ui/icons/AddCircle'; 6 | import DockerComposeInterface from './DockerComposeInterface' 7 | 8 | export default function DockerCompose(props){ 9 | 10 | const [ childKey,setChildKey ] = useState(0); 11 | 12 | return ( 13 |
14 | 15 |
16 | Docker Compose File 17 |
18 | 38 |
39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /client/components/Home/LoadExistingApplication.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import LoadExistingApplicationsDisplay from './LoadExistingApplicationsDisplay' 5 | import { Typography } from '@material-ui/core'; 6 | 7 | export default function LoadExistingApplications(props){ 8 | 9 | const [ childKey,setChildKey ] = useState(0); 10 | 11 | const handleOnClick = (e) =>{ 12 | // const newConnectionStringEle = (document.getElementById("mongoUri-input-field")) as HTMLInputElement; 13 | // const newConnectionString: string = newConnectionStringEle.value; 14 | // newConnectionStringEle.value = ''; 15 | // props.setConnectionString(newConnectionString) 16 | 17 | // //Force parent component to update. 18 | // const newChildKey: number = Math.floor(Math.random() * 100000); 19 | // props.setChildKey(newChildKey) 20 | } 21 | 22 | return ( 23 |
24 | 25 | Load Existing Application 26 | 27 | 39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /client/components/Home/CreateNewApplication.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; 4 | import CreateNewApplicationInput from './CreateNewApplicationInput' 5 | 6 | import Paper from '@material-ui/core/Paper'; 7 | import Button from '@material-ui/core/Button'; 8 | import { Typography, Grid } from '@material-ui/core'; 9 | 10 | const useStyles = makeStyles((theme:Theme) => 11 | createStyles({ 12 | button: { 13 | margin: theme.spacing(1), 14 | } 15 | }), 16 | ); 17 | 18 | export default function CreateExistingApplication(props){ 19 | const classes = useStyles() 20 | 21 | const [ childKey,setChildKey ] = useState(0); 22 | 23 | const handleOnClick = (e) =>{ 24 | const createNewAppEle = (document.getElementById("cerate-new-application-input-field")) as HTMLInputElement; 25 | const createNewAppString: string = createNewAppEle.value; 26 | createNewAppEle.value = ''; 27 | props.setNewApplication(createNewAppString) 28 | 29 | //Force parent component to update. 30 | const newChildKey: number = Math.floor(Math.random() * 100000); 31 | props.setChildKey(newChildKey) 32 | } 33 | 34 | return ( 35 | 36 | 42 | 51 | 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /client/components/Oak/RoutingInformation/RouteExecutionOrder.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import Paper from '@material-ui/core/Paper'; 5 | 6 | import RouteExcecutionOrderDisplay from './RouteExcecutionOrderDisplay'; 7 | 8 | export default function RouteExecutionOrder(props){ 9 | 10 | const [ childKey,setChildKey ] = useState(0); 11 | 12 | // const handleClick = (e) =>{ 13 | // const inputFieldEle = (document.getElementById("Specifiy DB Input field")) as HTMLInputElement; 14 | // props.setDBBeingModified(inputFieldEle.value) 15 | // if (!props.dbInputDisplay[inputFieldEle.value]){ 16 | // const newDBInputDisplay = props.dbInputDisplay; 17 | // newDBInputDisplay[inputFieldEle.value] = []; 18 | // props.setDBInputDisplay(newDBInputDisplay); 19 | // } 20 | // inputFieldEle.value = ''; 21 | // } 22 | 23 | return ( 24 |
25 | 26 |
27 | Route Execution Order 28 |
29 | 44 |
45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /client/components/Oak/ExistingEndPoints/ExistingEndPoints.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState } = React; 3 | 4 | import Paper from '@material-ui/core/Paper'; 5 | import AddCircleIcon from '@material-ui/icons/AddCircle'; 6 | 7 | import ExistingEndPointsDisplay from './ExistingEndpointsDisplay' 8 | import EndPointNameInput from './EndPointNameInput' 9 | 10 | 11 | export default function ExisitngEndPoints(props){ 12 | 13 | const handleClick = (e) =>{ 14 | const inputFieldEle = (document.getElementById("Specifiy Endpoint Name")) as HTMLInputElement; 15 | let inputVal: string = inputFieldEle.value; 16 | if(inputVal[0] !== '/') inputVal = '/' + inputVal; 17 | if (!props.endPoints[inputVal]){ 18 | const newEndPoint = props.endPoints; 19 | newEndPoint[inputVal] = []; 20 | props.setEndPoints(newEndPoint); 21 | props.setSelectedEndPoint(inputVal) 22 | //Force parent component to update. 23 | const newChildKey: number = Math.floor(Math.random() * 100000); 24 | props.setChildKey(newChildKey); 25 | } 26 | inputFieldEle.value = ''; 27 | } 28 | 29 | 30 | return ( 31 |
32 | 33 |
34 | Existing End Points 35 |
36 | 44 | 45 | handleClick(e)} 48 | /> 49 |
50 |
51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /templates/constructor.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "https://deno.land/std@0.83.0/fs/mod.ts"; 2 | import { 3 | prettier, 4 | prettierPlugins 5 | } from "https://denolib.com/denolib/prettier/prettier.ts"; 6 | 7 | import * as schemaBuilder from "./schemaBuilder.ts" 8 | 9 | 10 | interface PKG { 11 | schemaInterface: string; 12 | collectionString: string; 13 | credential?: string; 14 | importStr?: string; 15 | client?: string; 16 | dbname?: string; 17 | } 18 | 19 | interface Person { 20 | schemaName: string; 21 | schemaId: number; 22 | schemaProperties: string[]; 23 | } 24 | 25 | const data1: Person = { 26 | schemaName: "bamby", 27 | schemaId: 12, 28 | schemaProperties: [ 29 | "username: string", 30 | "password: string", 31 | "email: string", 32 | "age: number", 33 | ], 34 | }; 35 | 36 | 37 | 38 | 39 | const newImportStr = `import { MongoClient } from "https://deno.land/x/mongo@v0.22.0/mod.ts"` 40 | const newClient = `const client = new MongoClient()` 41 | const newDBName =`const db = client.database("${data.db}")` 42 | 43 | const interfacePromise = schemaBuilder.createInterface(data1) 44 | const collectionPromise = schemaBuilder.collectionFactory(data1) 45 | const credentialStr = schemaBuilder.credentialFactory(data) 46 | 47 | const data: PKG = { 48 | schemaInterface: interfacePromise, 49 | collectionString: collectionPromise, 50 | credential: credentialStr, 51 | importStr: newImportStr, 52 | client: newClient, 53 | dbname: newDBName 54 | } 55 | 56 | async function createDir(obj: PKG) { 57 | 58 | 59 | const template = 60 | `${obj.importStr};\n${obj.client};\n${obj.dbname};\n${obj.credential};\n${obj.schemaInterface};` 61 | 62 | const pretty = prettier.format(template, { 63 | parser: "babel", 64 | plugins: prettierPlugins 65 | }); 66 | await fs.ensureDir("./Models") 67 | await fs.ensureFile("./Models/models.ts") 68 | Deno.writeTextFile("./Models/models.ts", pretty + '\n' + obj.collectionString+';') 69 | 70 | } 71 | -------------------------------------------------------------------------------- /client/components/MongoDB/DBInput/DBInputFieldValue.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import TextField from '@material-ui/core/TextField'; 4 | 5 | export default function DBInputFieldValue(props){ 6 | 7 | const handleClick = (e) =>{ 8 | if(e.keyCode === 13){ 9 | e.preventDefault(); 10 | if(props.dbInputDisplay[props.dbBeingModified]){ 11 | const keyEle = (document.getElementById("key-input-field")) as HTMLInputElement; 12 | const newKeyValue = keyEle.value; 13 | keyEle.value = ''; 14 | const valueEle = (document.getElementById("value-input-field")) as HTMLInputElement; 15 | const newValueEle = valueEle.value; 16 | valueEle.value = ''; 17 | 18 | const newDBInputDisplay = props.dbInputDisplay; 19 | newDBInputDisplay[props.dbBeingModified].push([`${newKeyValue}: ${newValueEle};`]); 20 | props.setDBInputDisplay(newDBInputDisplay) 21 | 22 | const newEndPoint: string = '/' + props.dbBeingModified + ':' + newKeyValue; 23 | const newEndPoints = props.endPoints; 24 | newEndPoints[newEndPoint] = []; 25 | props.setEndPoints(newEndPoints); 26 | 27 | //Force parent component to update. 28 | const newChildKey2: number = props.childKey2 + 1; 29 | props.setChildKey2(newChildKey2) 30 | }else { 31 | alert('Please select an existing model.') 32 | const keyEle = (document.getElementById("key-input-field")) as HTMLInputElement; 33 | keyEle.value = ''; 34 | const valueEle = (document.getElementById("value-input-field")) as HTMLInputElement; 35 | valueEle.value = ''; 36 | } 37 | } 38 | } 39 | 40 | return ( 41 |
42 |
43 | 50 | 51 |
52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /client/components/Oak/RoutingInformation/ResponseStatus.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; 5 | import InputLabel from '@material-ui/core/InputLabel'; 6 | import MenuItem from '@material-ui/core/MenuItem'; 7 | import FormHelperText from '@material-ui/core/FormHelperText'; 8 | import FormControl from '@material-ui/core/FormControl'; 9 | import Select from '@material-ui/core/Select'; 10 | 11 | import ExistingModelsDisplay from './ExistingModelsDisplay.tsx' 12 | import DBNameInput from './DBNameInput.tsx'; 13 | 14 | const useStyles = makeStyles((theme: Theme) => 15 | createStyles({ 16 | formControl: { 17 | margin: theme.spacing(1), 18 | width: '20vw', 19 | }, 20 | }), 21 | ); 22 | 23 | export default function RoutingMethods(props){ 24 | const classes = useStyles(); 25 | 26 | const [ childKey,setChildKey ] = useState(0); 27 | 28 | const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { 29 | const resStatusEle = event.target as HTMLElement; 30 | const index: number = resStatusEle.value; 31 | props.setResStatus(statuses[index]); 32 | }; 33 | 34 | const labelDisplay = () => { 35 | if(props.resStatus == '') return 'Please select a response status'; 36 | else return props.resStatus; 37 | }; 38 | 39 | const statuses: Array = ['200 OK','201 Created','301 Moved','404 Not Found']; 40 | 41 | return ( 42 | 43 | {labelDisplay()} 44 | 55 | 56 | ); 57 | } -------------------------------------------------------------------------------- /templates/schemaBuilder.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "https://deno.land/std@0.83.0/fs/mod.ts"; 2 | import * as file from "./constants.ts"; 3 | import { envConstructor } from "./envConstructor.ts" 4 | 5 | interface Schema { 6 | schemaName: string; 7 | schemaId: number; 8 | schemaProperties: string[]; 9 | } 10 | 11 | interface Credentials { 12 | db: string; 13 | host: string; 14 | port: number; 15 | username: string; 16 | password: string; 17 | 18 | } 19 | 20 | interface envPKG { 21 | MONGO_HOST: string; 22 | MONGO_DB: string; 23 | MONGO_USER: string; 24 | MONGO_PASS: string; 25 | } 26 | 27 | const testPKG: envPKG = { 28 | MONGO_HOST: "cluster0.yybae.mongodb.net", 29 | MONGO_DB: "phlappjack", 30 | MONGO_USER: "dbUser", 31 | MONGO_PASS: "secret123", 32 | } 33 | 34 | export const createInterface = (model: Schema) => { 35 | const schema = model.schemaName.slice(0, 1).toUpperCase() + 36 | model.schemaName.slice(1) + "Schema"; 37 | const id = model.schemaId; 38 | let propString = ""; 39 | 40 | model.schemaProperties.forEach((el) => { 41 | const type = el.slice(el.indexOf(":") + 1); 42 | const key = el.slice(0, el.indexOf(":") + 1); 43 | 44 | propString += `${key}${type},`; 45 | }); 46 | 47 | const schemaInterface = `interface ${schema} { 48 | _id: { $oid: string }, ${propString} 49 | }`; 50 | 51 | return schemaInterface 52 | 53 | }; 54 | 55 | 56 | export const collectionFactory = (model: Schema) => { 57 | const collectionLabel = model.schemaName; 58 | const lableForExport = model.schemaName.slice(0, 1).toUpperCase() + 59 | model.schemaName.slice(1) + "Schema"; 60 | 61 | const collectionString = 62 | `const ${collectionLabel} = db.collection<${lableForExport}>("${collectionLabel}")`; 63 | 64 | return collectionString 65 | 66 | }; 67 | 68 | export const credentialFactory = async (model: Credentials) => { 69 | const { MONGO_HOST, MONGO_DB, MONGO_USER, MONGO_PASS } = await envConstructor(testPKG) 70 | 71 | const url: string = `mongodb+srv://${MONGO_USER}:${MONGO_PASS}@${MONGO_HOST}/${MONGO_DB}?authMechanism=SCRAM-SHA-1&retryWrites=true&w=majority` 72 | 73 | return url 74 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const nodeExternals = require('webpack-node-externals'); 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 4 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 5 | // const HtmlWebpackPlugin = require('html-webpack-plugin'); 6 | 7 | module.exports = { 8 | entry: './client/index.tsx', 9 | output: { 10 | path: path.resolve(__dirname, 'build'), 11 | filename: 'bundle.js', 12 | }, 13 | resolve: { 14 | extensions: [".js", ".jsx", ".json", ".ts", ".tsx"], 15 | }, 16 | // mode: process.env.NODE_ENV, 17 | mode: process.env.NODE_ENV, 18 | // externalsPresets: { node: true }, 19 | // externals: [nodeExternals()], 20 | devServer: { 21 | publicPath: '/build/', 22 | // hot: true, 23 | proxy: { 24 | '/api':'http://localhost:3000', 25 | '/ConfigureApplication':'http://localhost:3000', 26 | } 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(js|jsx|ts|tsx)$/, 32 | exclude: /node_modules/, 33 | use: { 34 | loader: 'babel-loader', 35 | options: { 36 | presets: [ 37 | '@babel/preset-env', 38 | '@babel/preset-react', 39 | "@babel/preset-typescript" 40 | ] 41 | } 42 | } 43 | }, 44 | // re-enable to check ts 45 | // { 46 | // test: /\.(ts|tsx)$/, 47 | // loader: "awesome-typescript-loader", 48 | // }, 49 | { 50 | test: /\.scss$/, 51 | exclude: /node_modules/, 52 | use: [ 53 | process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader', 54 | "css-loader", 55 | "sass-loader" 56 | ] 57 | }, 58 | ] 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /client/components/Oak/RoutingInformation/RoutingMethods.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import { createStyles, makeStyles, Paper} from '@material-ui/core'; 5 | import InputLabel from '@material-ui/core/InputLabel'; 6 | import MenuItem from '@material-ui/core/MenuItem'; 7 | import FormHelperText from '@material-ui/core/FormHelperText'; 8 | import FormControl from '@material-ui/core/FormControl'; 9 | import Select from '@material-ui/core/Select'; 10 | 11 | import ExistingModelsDisplay from './ExistingModelsDisplay' 12 | import DBNameInput from './DBNameInput'; 13 | 14 | const drawerWidth = 240; 15 | 16 | const useStyles = makeStyles((theme: Theme) => 17 | createStyles({ 18 | formControl: { 19 | margin: theme.spacing(1), 20 | width: '20vw', 21 | }, 22 | }), 23 | ); 24 | 25 | export default function RoutingMethods(props){ 26 | const classes = useStyles(); 27 | 28 | const [ childKey,setChildKey ] = useState(0); 29 | 30 | const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { 31 | const resEle = event.target as HTMLElement; 32 | const index: number = resEle.value; 33 | props.setResMethod(responses[index].toLowerCase()); 34 | //Force parent element to re-render. 35 | const newChildKey0: number = Math.floor(Math.random() * 100000); 36 | props.setChildKey0(newChildKey0); 37 | }; 38 | 39 | const labelDisplay = () => { 40 | if(props.resMethod == '') return 'Select a response method'; 41 | else return props.resMethod; 42 | }; 43 | 44 | const responses = ['DELETE','GET','PATCH','POST','PUT']; 45 | 46 | return ( 47 | 48 | {labelDisplay()} 49 | 60 | 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | lerna-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # TypeScript v1 declaration files 47 | typings/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Microbundle cache 59 | .rpt2_cache/ 60 | .rts2_cache_cjs/ 61 | .rts2_cache_es/ 62 | .rts2_cache_umd/ 63 | 64 | # Optional REPL history 65 | .node_repl_history 66 | 67 | # Output of 'npm pack' 68 | *.tgz 69 | 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | 73 | # dotenv environment variables file 74 | .env 75 | .env.test 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | 80 | # Next.js build output 81 | .next 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Create Applications 109 | Created Applications/ 110 | 111 | # .exe 112 | *.exe 113 | -------------------------------------------------------------------------------- /client/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { AppBar, Grid, Toolbar, Typography, IconButton, createStyles} from '@material-ui/core'; 3 | import MenuIcon from '@material-ui/icons/Menu'; 4 | import { makeStyles, Theme } from '@material-ui/core/styles' 5 | import clsx from 'clsx' 6 | 7 | const drawerWidth = 240; 8 | 9 | const useStyles = makeStyles((theme: Theme) => 10 | createStyles({ 11 | menuButton: { 12 | marginRight: theme.spacing(2), 13 | }, 14 | hide: { 15 | display: 'none', 16 | }, 17 | appBar: { 18 | transition: theme.transitions.create(['margin', 'width'], { 19 | easing: theme.transitions.easing.sharp, 20 | duration: theme.transitions.duration.leavingScreen, 21 | }), 22 | }, 23 | appBarShift: { 24 | width: `calc(100% - ${drawerWidth}px)`, 25 | marginLeft: drawerWidth, 26 | transition: theme.transitions.create(['margin', 'width'], { 27 | easing: theme.transitions.easing.easeOut, 28 | duration: theme.transitions.duration.enteringScreen, 29 | }), 30 | }, 31 | })); 32 | 33 | 34 | 35 | export default function Header(props){ 36 | const classes = useStyles(); 37 | 38 | const onRender = () =>{ 39 | if(props.newApplication) return String('Working on ' + props.newApplication) 40 | else return 'Please load or create an application'; 41 | } 42 | 43 | 44 | return ( 45 | 48 | 49 | 56 | 57 | 58 | 59 | Phlappjack 60 | {onRender()} 61 | 62 | 63 | 64 | ) 65 | } -------------------------------------------------------------------------------- /client/components/Docker/_DockerFile.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import Paper from '@material-ui/core/Paper'; 5 | import AddCircleIcon from '@material-ui/icons/AddCircle'; 6 | import _DockerFileInterface from './_DockerFileInterface' 7 | 8 | export default function _DockerFile(props){ 9 | 10 | const [ childKey,setChildKey ] = useState(0); 11 | 12 | const handleClick = (e) =>{ 13 | const inputFieldEle = (document.getElementById("Specifiy DB Input field")) as HTMLInputElement; 14 | props.setDBBeingModified(inputFieldEle.value) 15 | if (!props.dbInputDisplay[inputFieldEle.value]){ 16 | const newDBInputDisplay = props.dbInputDisplay; 17 | newDBInputDisplay[inputFieldEle.value] = []; 18 | props.setDBInputDisplay(newDBInputDisplay); 19 | } 20 | inputFieldEle.value = ''; 21 | } 22 | 23 | return ( 24 |
25 | 26 |
27 | Docker File 28 |
29 | <_DockerFileInterface 30 | key ={props.childKey} 31 | setChildKey = {props.setChildKey} 32 | dbBeingModified = {props.dbBeingModified} 33 | setDBBeingModified = {props.setDBBeingModified} 34 | dbInputDisplay = {props.dbInputDisplay} 35 | setDBInputDisplay = {props.setDBInputDisplay} 36 | atlasUserName = {props.atlasUserName} 37 | setAtlasUserName = {props.setAtlasUserName} 38 | atlasPassword = {props.atlasPassword} 39 | setAtlasPassword = {props.setAtlasPassword} 40 | atlasHostCluster = {props.atlasHostCluster} 41 | setAtlasHostCluster = {props.setAtlasHostCluster} 42 | atlasDB = {props.atlasDB} 43 | setAtlasDB = {props.setAtlasDB} 44 | dockerFile = {props.dockerFile} 45 | setDockerFile = {props.setDockerFile} 46 | dockerCompose = {props.dockerCompose} 47 | setDockerCompose = {props.setDockerCompose} 48 | /> 49 |
50 |
51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /openChrome.ts: -------------------------------------------------------------------------------- 1 | export default async function openChrome (osType) { 2 | 3 | if (osType === 'linux') { 4 | 5 | const cmd = Deno.run({ 6 | cmd: ['/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe', 'http://localhost:8000'], 7 | stdout: "piped", 8 | stderr: "piped", 9 | },) 10 | 11 | const { code } = await cmd.status() 12 | 13 | const rawOutput = await cmd.output(); 14 | const rawError = await cmd.stderrOutput(); 15 | 16 | 17 | if (code === 0) { 18 | const output = await Deno.stdout.write(rawOutput); 19 | console.log("Opening Chrome...") 20 | } else { 21 | const errorString = new TextDecoder().decode(rawError); 22 | console.log(errorString); 23 | } 24 | 25 | } else if (osType === 'windows'){ 26 | 27 | const cmd = Deno.run({ 28 | cmd: ['C:/Program Files (x86)/Google/Chrome/Application/chrome.exe', 'http://localhost:8000'], 29 | stdout: "piped", 30 | stderr: "piped", 31 | },) 32 | 33 | const { code } = await cmd.status() 34 | 35 | const rawOutput = await cmd.output(); 36 | const rawError = await cmd.stderrOutput(); 37 | 38 | 39 | if (code === 0) { 40 | const output = await Deno.stdout.write(rawOutput); 41 | console.log("Opening Chrome...") 42 | } else { 43 | const errorString = new TextDecoder().decode(rawError); 44 | console.log(errorString); 45 | } 46 | 47 | } else if (osType === 'darwin'){ 48 | 49 | const cmd = Deno.run({ 50 | cmd: ['/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome', 'http://localhost:8000'], 51 | stdout: "piped", 52 | stderr: "piped", 53 | },) 54 | 55 | const { code } = await cmd.status() 56 | 57 | const rawOutput = await cmd.output(); 58 | const rawError = await cmd.stderrOutput(); 59 | 60 | 61 | if (code === 0) { 62 | const output = await Deno.stdout.write(rawOutput); 63 | console.log("Opening Chrome...") 64 | } else { 65 | const errorString = new TextDecoder().decode(rawError); 66 | console.log(errorString); 67 | } 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.tsx", 6 | "scripts": { 7 | "start": "NODE_ENV=production node server/server.js", 8 | "build": "NODE_ENV=production webpack", 9 | "dev": "NODE_ENV=development webpack serve --open && nodemon ./server.js", 10 | "gulp-prod": "node_modules/.bin/gulp prod", 11 | "gulp-dev": "node_modules/.bin/gulp dev" 12 | }, 13 | "nodemonConfig": { 14 | "ignore": [ 15 | "build", 16 | "client" 17 | ] 18 | }, 19 | "author": "CodesmithLLC https://github.com/CodesmithLLC ", 20 | "license": "ISC", 21 | "dependencies": { 22 | "@babel/preset-typescript": "^7.13.0", 23 | "@material-ui/core": "^4.11.4", 24 | "@material-ui/icons": "^4.11.2", 25 | "@types/jest": "^26.0.23", 26 | "@types/node": "^15.12.1", 27 | "body-parser": "^1.19.0", 28 | "browserify": "^17.0.0", 29 | "cors": "^2.8.5", 30 | "express": "^4.17.1", 31 | "fs": "0.0.1-security", 32 | "fs-extra": "^10.0.0", 33 | "gulp": "^4.0.2", 34 | "path": "^0.12.7", 35 | "prop-types": "^15.6.1", 36 | "react": "^17.0.2", 37 | "react-beautiful-dnd": "^13.1.0", 38 | "react-dom": "^17.0.2", 39 | "react-icons": "^4.2.0", 40 | "react-router-dom": "^5.2.0" 41 | }, 42 | "devDependencies": { 43 | "@babel/core": "^7.14.0", 44 | "@babel/preset-env": "^7.14.0", 45 | "@babel/preset-react": "^7.13.13", 46 | "@types/react": "^17.0.9", 47 | "@types/react-dom": "^17.0.6", 48 | "awesome-typescript-loader": "^5.2.1", 49 | "babel-loader": "^8.2.2", 50 | "concurrently": "^6.0.2", 51 | "copy-webpack-plugin": "^9.0.0", 52 | "cross-env": "^7.0.3", 53 | "css-loader": "^5.2.6", 54 | "html-webpack-plugin": "^5.3.1", 55 | "isomorphic-fetch": "^3.0.0", 56 | "mini-css-extract-plugin": "^1.6.0", 57 | "nodemon": "^2.0.7", 58 | "sass": "^1.32.12", 59 | "sass-loader": "^11.0.1", 60 | "source-map-loader": "^3.0.0", 61 | "style-loader": "^2.0.0", 62 | "typescript": "^3.9.9", 63 | "webpack": "^5.38.1", 64 | "webpack-cli": "^4.7.0", 65 | "webpack-dev-server": "^3.11.2", 66 | "webpack-node-externals": "^3.0.0" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /client/components/Oak/RoutingInformation/RoutingInformation.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import Paper from '@material-ui/core/Paper'; 5 | import AddCircleIcon from '@material-ui/icons/AddCircle'; 6 | 7 | import ExistingModelsDisplay from './ExistingModelsDisplay.tsx' 8 | import RoutingMethods from './RoutingMethods.tsx'; 9 | import ResponseStatus from './ResponseStatus.tsx'; 10 | import Middleware from './Middleware.tsx'; 11 | 12 | export default function RoutingInformation(props){ 13 | 14 | const [ childKey0,setChildKey0 ] = useState(0); 15 | const [ childKey1,setChildKey1 ] = useState(0); 16 | const [ resMethod, setResMethod ] = useState('') 17 | 18 | const handleClick = (e) =>{ 19 | 20 | const newRoutes = props.routes; 21 | const newRouteEle = document.getElementById('middleware-input') as HTMLInputElement; 22 | const newRouteEleValue = newRouteEle.value 23 | newRoutes.push(newRouteEleValue) 24 | props.setRoutes(newRoutes); 25 | props.setMiddleWareTemp('') 26 | 27 | //Force parent element to re-render. 28 | const newChildKey: number = Math.floor(Math.random() * 100000); 29 | props.setChildKey(newChildKey); 30 | } 31 | 32 | return ( 33 |
34 | 35 |
36 | {`Selected Route: ${props.selectedEndPoint}`} 37 |
38 |
39 | 45 |
46 |
47 | 57 |
58 | 62 |
63 |
64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /AppConfiguration/CRUDFunctions.ts: -------------------------------------------------------------------------------- 1 | 2 | interface Schema { 3 | schemaName: string 4 | properties: string[] 5 | } 6 | 7 | export const CRUDFunctionGet = (schema) => { 8 | 9 | const template: string = `const getAll${schema} = async (ctx: RouterContext) => { 10 | const ${schema} = await ${schema}.find(); 11 | ctx.response.body = ${schema}; 12 | }\n` 13 | 14 | return template 15 | } 16 | 17 | export const CRUDFunctionGetOne = (schema) => { 18 | 19 | const template: string = `const get${schema} = async (ctx: RouterContext) => { 20 | const id = ctx.params.id; 21 | const ${schema} = await ${schema}.findOne({_id: {$oid: id } }); 22 | ctx.response.body = ${schema}; 23 | }\n` 24 | 25 | return template 26 | } 27 | 28 | export const CRUDFunctionCreateOne = (schema, props) => { 29 | 30 | const single: string = schema.slice(0,schema.length-1) 31 | 32 | const template: string = `const create${schema} = async (ctx: RouterContext) => { 33 | const {${props}} = await ctx.request.body().value; 34 | const ${single}: any = { 35 | ${props} 36 | }; 37 | const id = await ${schema}.insertOne(); 38 | ${single}._id = id; 39 | ctx.response.status = 201 40 | ctx.response.body = ${single} 41 | }\n` 42 | 43 | return template 44 | } 45 | 46 | export const CRUDFunctionPatch = (schema, props) => { 47 | 48 | const single: string = schema.slice(0,schema.length-1) 49 | 50 | 51 | const template: string = `const update${schema} = async (ctx: RouterContext) => { 52 | 53 | const id = ctx.params.id 54 | const { ${props} } = await ctx.request.body().value; 55 | const { modified } = await ${schema}.updateOne({ _id: { $oid: id } }, { 56 | $set: { 57 | ${props} 58 | } 59 | }) 60 | if (!modified) { 61 | ctx.response.status = 404; 62 | ctx.response.body = { message: '${single} not found' } 63 | return; 64 | } 65 | ctx.response.body = await ${schema}.findOne({ _id: { $oid: id } }) 66 | }\n` 67 | 68 | return template 69 | 70 | } 71 | 72 | export const CRUDFunctionDelete = (schema) => { 73 | 74 | const single: string = schema.slice(0,schema.length-1) 75 | 76 | const template: string = ` const delete${single} = async (ctx: RouterContext) => { 77 | const id = ctx.params.id 78 | 79 | const ${single} = await ${schema}.deleteOne({ _id: { $oid: id } }); 80 | 81 | if (!${single}) { 82 | ctx.response.status = 404; 83 | ctx.response.body = { message: '${single} not found' } 84 | return; 85 | } 86 | ctx.response.status = 204; 87 | }` 88 | 89 | return template 90 | } -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | import { Application, send, Router } from "./deps.ts"; 2 | import { createBundle } from "./createBundle.ts"; 3 | import gitClone from "./gitfunctions/gitClone.ts" 4 | import gitCommitPush from "./gitfunctions/gitCommitPush.ts" 5 | import { Leaf } from "./deps.ts"; 6 | import { configureApplication } from "./AppConfiguration/appConfigurator.ts" 7 | import openChrome from "./openChrome.ts" 8 | 9 | // build bundle console messages, single line stdout 10 | const messageBuilding = new TextEncoder().encode("Building Bundle..."); 11 | const messageDone = new TextEncoder().encode("Done!\n"); 12 | 13 | await Deno.writeAll(Deno.stdout, messageBuilding); 14 | // build bundle 15 | // await createBundle(); 16 | await Deno.writeAll(Deno.stdout, messageDone); 17 | 18 | const index = Leaf.readTextFileSync("./build/index.html") 19 | const bundle = Leaf.readTextFileSync("./build/bundle.js") 20 | const logo = Leaf.readTextFileSync("./build/logo.svg") 21 | 22 | //open chrome 23 | await openChrome(Deno.build.os) 24 | 25 | const router = new Router(); 26 | router 27 | .get("/", (context) => { 28 | context.response.body = index; 29 | }) 30 | .get("/bundle.js", (context) => { 31 | context.response.headers.set("Content-Type", "application/javascript; charset=utf-8") 32 | context.response.body = bundle; 33 | }) 34 | .get("/logo.svg", (context) => { 35 | context.response.headers.set("Content-Type", "image/svg+xml") 36 | context.response.body = logo; 37 | }) 38 | .post("/export", async (context) => { 39 | const response = await context.request.body(); 40 | const props = await response.value; 41 | const dir = './Created Applications'; 42 | const { newApplication, atlasHostCluster, atlasUserName, atlasPassword, atlasDB, dbInputDisplay, dockerFile, dockerCompose, routes } = props; 43 | configureApplication(dir, newApplication, atlasHostCluster, atlasUserName, atlasPassword, atlasDB, dbInputDisplay, dockerFile, dockerCompose, routes); 44 | }) 45 | .post("/gitclone/:app", async (context) => { 46 | const dir = '/Created Applications'; 47 | const appDirectory = `${dir}/${context.params.app}` 48 | console.log(appDirectory) 49 | const request = context.request.body() 50 | const repoUrl = await request.value 51 | console.log(repoUrl) 52 | gitClone(appDirectory, repoUrl) 53 | context.response.body = "Cloned repo!"; 54 | }) 55 | .post("/gitpush/:app", async (context) => { 56 | const dir = './Created Applications'; 57 | const appDirectory = `${dir}/${context.params.app}` 58 | const request = context.request.body() 59 | const repoUrl = await request.value 60 | gitCommitPush(appDirectory, repoUrl) 61 | context.response.body = "Pushed repo!"; 62 | }) 63 | 64 | 65 | const app = new Application(); 66 | app.use(router.routes()); 67 | app.use(router.allowedMethods()); 68 | app.addEventListener("error", (evt) => { 69 | // Will log the thrown error to the console. 70 | console.log(evt.error); 71 | }); 72 | 73 | 74 | console.log(`Listening on http://localhost:8000`); 75 | await app.listen({ port: 8000 }); 76 | -------------------------------------------------------------------------------- /client/components/MongoDB/MongoAtlasConnection/MongoAtlasConnection.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import Paper from '@material-ui/core/Paper'; 5 | import AddCircleIcon from '@material-ui/icons/AddCircle'; 6 | 7 | import MongoUserNameDisplay from './MongoUserNameDisplay' 8 | import MongoUserNameInput from './MongoUserNameInput'; 9 | import MongoPasswordDisplay from './MongoPasswordDisplay'; 10 | import MongoPasswordInput from './MongoPasswordInput'; 11 | import MongoHostDisplay from './MongoHostDisplay'; 12 | import MongoHostInput from './MongoHostInput'; 13 | import MongoDBDisplay from './MongoDBDisplay' 14 | import MongoDBInput from './MongoDBInput'; 15 | 16 | export default function MongoAtlasConnection(props){ 17 | 18 | const [ childKey,setChildKey ] = useState(0); 19 | 20 | const handleOnClick = (e) =>{ 21 | // const newConnectionStringEle = (document.getElementById("mongoUri-input-field")) as HTMLInputElement; 22 | // const newConnectionString: string = newConnectionStringEle.value; 23 | // newConnectionStringEle.value = ''; 24 | // props.setConnectionString(newConnectionString) 25 | 26 | // //Force parent component to update. 27 | // const newChildKey: number = Math.floor(Math.random() * 100000); 28 | // props.setChildKey(newChildKey) 29 | } 30 | 31 | return ( 32 |
33 | 34 |
35 | Mongo Atlas Connection 36 |
37 |
38 | 42 | 46 |
47 |
48 | 52 | 56 |
57 |
58 | 62 | 66 |
67 |
68 | 72 | 76 |
77 |
78 |
79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /client/components/MongoDB/ExistingModels/ExistingModels.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import Paper from '@material-ui/core/Paper'; 5 | import AddCircleIcon from '@material-ui/icons/AddCircle'; 6 | 7 | import ExistingModelsDisplay from './ExistingModelsDisplay' 8 | import DBNameInput from './DBNameInput'; 9 | 10 | export default function ExistingModels(props){ 11 | 12 | const [childKey0, setChildKey0 ] = useState(0); 13 | 14 | const handleClick = (e) =>{ 15 | const inputFieldEle = (document.getElementById("Specifiy DB Input field")) as HTMLInputElement; 16 | props.setDBBeingModified(inputFieldEle.value) 17 | if (!props.dbInputDisplay[inputFieldEle.value]){ 18 | const newDBInputDisplay = props.dbInputDisplay; 19 | newDBInputDisplay[inputFieldEle.value] = []; 20 | props.setDBInputDisplay(newDBInputDisplay); 21 | 22 | const newEndPoint: string = String('/' + inputFieldEle.value); 23 | const newEndPoints = props.endPoints; 24 | newEndPoints[newEndPoint] = []; 25 | props.setEndPoints(newEndPoints); 26 | 27 | const newIdEndPoint: string = String('/' + inputFieldEle.value + ':id'); 28 | newEndPoints[newIdEndPoint] = []; 29 | props.setEndPoints(newEndPoints); 30 | 31 | const newDBToggleState = new Array(Object.keys(props.dbInputDisplay).length).fill(true).map((item, idx) => true); 32 | props.setDBToggles(newDBToggleState); 33 | } 34 | inputFieldEle.value = ''; 35 | } 36 | 37 | return ( 38 |
39 | 40 |
41 | Existing Models 42 |
43 | 59 | 69 | handleClick(e)} 72 | /> 73 |
74 |
75 | ); 76 | } 77 | 78 | -------------------------------------------------------------------------------- /client/components/Home/Home.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; 3 | import { Typography } from '@material-ui/core'; 4 | import Paper from '@material-ui/core/Paper'; 5 | import Box from '@material-ui/core/Box'; 6 | import Grid from '@material-ui/core/Grid'; 7 | import LoadExistingApplication from './LoadExistingApplication' 8 | import CreateNewApplication from './CreateNewApplication' 9 | import clsx from 'clsx' 10 | 11 | const useStyles = makeStyles((theme:Theme) => 12 | createStyles({ 13 | paper: { 14 | margin: theme.spacing(0), 15 | padding: theme.spacing(4), 16 | // display:'flex', 17 | height:'80vh', 18 | flexGrow:1, 19 | }, 20 | image: { 21 | margin: theme.spacing(8) 22 | }, 23 | menupair: { 24 | maxWidth: '700px', 25 | }, 26 | load: { 27 | alignSelf:"flex-start" 28 | }, 29 | hide: { 30 | display: 'none', 31 | }, 32 | show: { 33 | display: 'reset', 34 | }, 35 | }), 36 | ); 37 | 38 | export default function Home(props){ 39 | 40 | const classes = useStyles(); 41 | const [ childKey,setChildKey ] = useState(0); 42 | 43 | return ( 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 65 | 66 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | ) 80 | } -------------------------------------------------------------------------------- /client/components/Oak/ExistingEndPoints/ExistingEndpointsDisplay.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStyles, Divider, Paper, List,ListItem, ListItemText, IconButton} from '@material-ui/core'; 3 | import { makeStyles, Theme } from '@material-ui/core/styles'; 4 | import Fab from '@material-ui/core/Fab'; 5 | import AddIcon from '@material-ui/icons/Add'; 6 | import DeleteIcon from '@material-ui/icons/Delete'; 7 | 8 | const useStyles = makeStyles((theme:Theme) => 9 | createStyles({ 10 | listItem: { 11 | display:"flex", 12 | justifyContent:"space-between" 13 | }, 14 | listItemText:{ 15 | fontSize:'2em', 16 | textAlign:'Left' 17 | } 18 | }), 19 | ); 20 | 21 | export default function ExistingEndpointsDisplay(props){ 22 | 23 | const classes = useStyles(); 24 | 25 | const handleSelection = (e) =>{ 26 | e.preventDefault(); 27 | const selectedEndPointName: string = e.target.innerText; 28 | props.setSelectedEndPoint(selectedEndPointName); 29 | } 30 | 31 | const handleMouseDown = (e) =>{ 32 | e.preventDefault(); 33 | let mouseup = false; 34 | document.addEventListener("mouseup", 35 | () => { 36 | mouseup = true; 37 | }, {once:true} 38 | ); 39 | const selectedKeyValueEle = (e.target) as HTMLDivElement; 40 | let redPercentage: number = 0; 41 | const intervalID = setInterval(() => { 42 | redPercentage += 1; 43 | if(redPercentage === 100){ 44 | clearInterval(intervalID); 45 | const indexToRemove: string = selectedKeyValueEle.parentNode.parentNode.id.split('_')[1]; 46 | const newEndPointNames = props.endPoints; 47 | delete newEndPointNames[indexToRemove]; 48 | props.setEndPoints(newEndPointNames); 49 | //Force parent component to update. 50 | const newChildKey: number = Math.floor(Math.random() * 100000); 51 | props.setChildKey(newChildKey); 52 | }else if(mouseup === false && redPercentage > 10){ 53 | selectedKeyValueEle.style.background = `linear-gradient(90deg, #ffffff ${100 - redPercentage}%, #ff0000 ${redPercentage}%)` 54 | } else if (mouseup === true){ 55 | selectedKeyValueEle.style.background = `transparent` 56 | clearInterval(intervalID); 57 | } 58 | }, 25) 59 | } 60 | 61 | const endPointNames = Object.keys(props.endPoints).sort(); 62 | 63 | return ( 64 |
65 | 66 | 67 | 68 | {endPointNames.map((text, index) => ( 69 | handleSelection(e)} 73 | onMouseDown = {(e) => handleMouseDown(e)} 74 | > 75 | handleSelection(e)} 79 | /> 80 | 81 | ))} 82 | 83 | 84 |
85 | 86 | ) 87 | } -------------------------------------------------------------------------------- /client/components/Docker/Docker.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; 3 | import Paper from '@material-ui/core/Paper'; 4 | import Grid from '@material-ui/core/Grid'; 5 | 6 | import _DockerFile from './_DockerFile' 7 | import DockerCompose from './DockerCompose' 8 | 9 | const useStyles = makeStyles((theme:Theme) => 10 | createStyles({ 11 | paper: { 12 | margin: theme.spacing(0), 13 | padding: theme.spacing(4), 14 | display:'flex', 15 | width:'65vw' 16 | }, 17 | }), 18 | ); 19 | 20 | export default function Docker(props){ 21 | 22 | const classes = useStyles(); 23 | const [ childKey,setChildKey ] = useState(0); 24 | 25 | return ( 26 | 27 | 28 | 29 | 30 | 31 | <_DockerFile 32 | key ={props.childKey} 33 | setChildKey = {props.setChildKey} 34 | dbBeingModified = {props.dbBeingModified} 35 | setDBBeingModified = {props.setDBBeingModified} 36 | dbInputDisplay = {props.dbInputDisplay} 37 | setDBInputDisplay = {props.setDBInputDisplay} 38 | atlasUserName = {props.atlasUserName} 39 | setAtlasUserName = {props.setAtlasUserName} 40 | atlasPassword = {props.atlasPassword} 41 | setAtlasPassword = {props.setAtlasPassword} 42 | atlasHostCluster = {props.atlasHostCluster} 43 | setAtlasHostCluster = {props.setAtlasHostCluster} 44 | atlasDB = {props.atlasDB} 45 | setAtlasDB = {props.setAtlasDB} 46 | dockerFile = {props.dockerFile} 47 | setDockerFile = {props.setDockerFile} 48 | dockerCompose = {props.dockerCompose} 49 | setDockerCompose = {props.setDockerCompose} 50 | /> 51 | 52 | 53 | 73 | 74 | 75 | 76 | 77 | 78 | ) 79 | } -------------------------------------------------------------------------------- /client/components/MongoDB/DBInput/DBInputFieldDisplay.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState } = React; 3 | 4 | import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; 5 | import List from '@material-ui/core/List'; 6 | import Divider from '@material-ui/core/Divider'; 7 | import ListItem from '@material-ui/core/ListItem'; 8 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 9 | import ListItemText from '@material-ui/core/ListItemText'; 10 | import Paper from '@material-ui/core/Paper'; 11 | 12 | const useStyles = makeStyles((theme: Theme) => 13 | createStyles({ 14 | listItemTextParenethese:{ 15 | fontSize:'2em', 16 | textAlign:'left', 17 | }, 18 | listItemText:{ 19 | fontSize:'1.5em', 20 | textIndent:'1em', 21 | } 22 | }), 23 | ); 24 | 25 | export default function DBInputFieldDisplay(props){ 26 | 27 | const classes = useStyles(); 28 | 29 | const handleMouseDown = (e) =>{ 30 | e.preventDefault(); 31 | let mouseup = false; 32 | document.addEventListener("mouseup", 33 | () => { 34 | mouseup = true; 35 | }, {once:true} 36 | ); 37 | const selectedKeyValueEle = (e.target) as HTMLDivElement; 38 | let redPercentage: number = 0; 39 | const intervalID = setInterval(() => { 40 | redPercentage += 1; 41 | if(redPercentage === 100){ 42 | clearInterval(intervalID); 43 | const indexToRemove: number = selectedKeyValueEle.parentNode.parentNode.id.split('_')[1]; 44 | const newDBInputDisplay = props.dbInputDisplay; 45 | newDBInputDisplay[props.dbBeingModified].splice(indexToRemove,1); 46 | props.setDBInputDisplay(newDBInputDisplay); 47 | //Force parent component to update. 48 | const newChildKey0: number = props.newChildKey + 1; 49 | props.setChildKey0(newChildKey0) 50 | }else if(mouseup === false && redPercentage > 20){ 51 | selectedKeyValueEle.style.background = `linear-gradient(90deg, #ffffff ${100 - redPercentage}%, #ff0000 ${redPercentage}%)` 52 | } else if (mouseup === true){ 53 | selectedKeyValueEle.style.background = `transparent` 54 | clearInterval(intervalID); 55 | } 56 | },25) 57 | } 58 | 59 | let displayFields = [] 60 | if(props.dbInputDisplay[props.dbBeingModified]){ 61 | displayFields = props.dbInputDisplay[props.dbBeingModified]; 62 | } 63 | 64 | return ( 65 |
66 | 67 | 68 | 69 | 73 | 74 | {displayFields.map((text, index) => ( 75 | handleMouseDown(e)} 79 | > 80 | 84 | ))} 85 | 86 | 90 | 91 | 92 | 93 |
94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /client/components/MongoDB/MongoDB.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; 3 | import Paper from '@material-ui/core/Paper'; 4 | import Grid from '@material-ui/core/Grid'; 5 | import DBInputField from './DBInput/DBInputField' 6 | import ExistingModels from './ExistingModels/ExistingModels' 7 | import MongoAtlasConnection from './MongoAtlasConnection/MongoAtlasConnection' 8 | 9 | 10 | const useStyles = makeStyles((theme:Theme) => 11 | createStyles({ 12 | paper: { 13 | margin: theme.spacing(1), 14 | padding: theme.spacing(6), 15 | flex: 1 16 | }, 17 | }), 18 | ); 19 | 20 | 21 | export default function MongoDB(props){ 22 | 23 | const classes = useStyles(); 24 | const [ childKey,setChildKey ] = useState(0); 25 | 26 | 27 | return ( 28 | 29 | 30 | 31 | 32 | 48 | 49 | 50 | 64 | 65 | 66 | 78 | 79 | 80 | 81 | 82 | ) 83 | } -------------------------------------------------------------------------------- /client/components/Oak/Oak.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStyles, Grid, Paper} from '@material-ui/core'; 3 | import { makeStyles, Theme } from '@material-ui/core/styles'; 4 | 5 | import ExistingEndpoints from './ExistingEndPoints/ExistingEndpointsDisplay'; 6 | import EndPointNameInput from './ExistingEndPoints/EndPointNameInput'; 7 | import ExistingEndPoints from './ExistingEndPoints/ExistingEndPoints' 8 | import RoutingInformation from './RoutingInformation/RoutingInformation' 9 | import RouteExecutionOrder from './RoutingInformation/RouteExecutionOrder'; 10 | 11 | const useStyles = makeStyles((theme:Theme) => 12 | createStyles({ 13 | paper: { 14 | margin: theme.spacing(1), 15 | padding: theme.spacing(6), 16 | flex: 1 17 | }, 18 | }), 19 | ); 20 | 21 | export default function Oak(props){ 22 | 23 | const classes = useStyles(); 24 | 25 | return ( 26 | 27 | 28 | 29 | 30 | 38 | 39 | 40 | 54 | 55 | 56 | 72 | 73 | 74 | 75 | 76 | ) 77 | } -------------------------------------------------------------------------------- /client/components/MongoDB/DBInput/DBInputField.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import Paper from '@material-ui/core/Paper'; 5 | import AddCircleIcon from '@material-ui/icons/AddCircle'; 6 | 7 | import DBInputFieldDisplay from './DBInputFieldDisplay'; 8 | import DBInputFieldKey from './DBInputFieldKey'; 9 | import DBInputFieldValue from './DBInputFieldValue'; 10 | 11 | export default function DBInputField(props){ 12 | 13 | const [ childKey0,setChildKey0] = useState(0); 14 | const [ childKey1,setChildKey1] = useState(0); 15 | const [ childKey2,setChildKey2] = useState(0); 16 | 17 | const handleOnClick = (e) =>{ 18 | if(props.dbInputDisplay[props.dbBeingModified]){ 19 | const keyEle = (document.getElementById("key-input-field")) as HTMLInputElement; 20 | const newKeyValue = keyEle.value; 21 | keyEle.value = ''; 22 | const valueEle = (document.getElementById("value-input-field")) as HTMLInputElement; 23 | const newValueEle = valueEle.value; 24 | valueEle.value = ''; 25 | 26 | const newDBInputDisplay = props.dbInputDisplay; 27 | newDBInputDisplay[props.dbBeingModified].push([`${newKeyValue}: ${newValueEle};`]); 28 | props.setDBInputDisplay(newDBInputDisplay) 29 | 30 | const newEndPoint: string = '/' + props.dbBeingModified + ':' + newKeyValue; 31 | const newEndPoints = props.endPoints; 32 | newEndPoints[newEndPoint] = []; 33 | props.setEndPoints(newEndPoints); 34 | //Force parent component to update. 35 | const newChildKey: number = Math.floor(Math.random() * 100000); 36 | props.setChildKey(newChildKey) 37 | }else { 38 | alert('Please select an existing model.') 39 | const keyEle = (document.getElementById("key-input-field")) as HTMLInputElement; 40 | keyEle.value = ''; 41 | const valueEle = (document.getElementById("value-input-field")) as HTMLInputElement; 42 | valueEle.value = ''; 43 | } 44 | } 45 | 46 | return ( 47 |
48 | 49 |
50 | {props.dbBeingModified} 51 |
52 | 60 |
61 | 69 | 79 |
80 | handleOnClick(e)}/> 81 |
82 |
83 | ); 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Phlappjack 2 | 3 | [![Leaf CI](https://github.com/mandarineorg/leaf/workflows/Unit%20Tests/badge.svg)](https://github.com/mandarineorg/leaf) 4 | 5 | ![image](./assets/logo.svg) 6 | 7 | The tastiest way to build your stack... in Deno Deploy! 8 | 9 | **No installation required** 10 | 11 | Head to www.phlappjack.com to download the executable for your OS. 12 | 13 | --- 14 | 15 | ## Description 16 | 17 | A GUI that provides middleware and back-end configuration to your existing front-end. Allows for rapid configuration of Oak/Mongo. Completed configuration can be exported to your local machine for further editing, turned into microservices with Docker Swarm or delivered to Deno Deploy CDN for an instantly live public app. 18 | 19 | ## Usage 20 | 21 | To use simply: 22 | 23 | 1. Download the executable and run it. 24 | 2. Phlappjack will open your Chrome browser on http://localhost:8000 25 | 3. Create a new application, or retrieve a previously saved one. 26 | 4. Utilize the Phlappjack modules to enter your Mongo DB information (THIS WILL NOT BE SAVED ON OUR END), your desired schemas and their properites (TypeScript formatted), and create working middleware functions (Note: these are supplised as working boiler plate functions that can be specified further in the editor). 27 | 5. NOTE: the schema information will need to exactly match your current working front end in case sensitivity. 28 | 6. Hit `SAVE`. 29 | 7. `EXPORT` will dump the files to your local system. 30 | 8. The `DEPLOY` button in Deno Deploy will push your app to your linked repository and to your project on Deno Deploy. 31 | 32 | 33 | *Your configuration will be output alongside the application in the `/createdApplication` directory.* 34 | 35 | ## Development 36 | 37 | The frontend is developed in Node and is using Webpack dev server to make the deployment process as quick and painless as possible. To work on the front end run the below: 38 | 39 | ### Frontend dev (Node) 40 | 41 | ```shell 42 | npm install 43 | npm run dev 44 | ``` 45 | 46 | ### Running the server in Deno 47 | 48 | ```shell 49 | deno run --unstable --allow-read --allow-write --allow-net mod.ts 50 | ``` 51 | 52 | Optionally run with the `--watch` flag for file reloading. 53 | 54 | This will serve the static react content stored in `/build` and watch for file changes. 55 | 56 | ### Creating a client Bundle 57 | 58 | Any changes to the frontend will need to be re-built to be reflected in the app. 59 | 60 | ```shell 61 | npm run build 62 | ``` 63 | ### Compiling the app executable 64 | 65 | We are using Leaf by [Mandarine](https://deno.land/x/mandarinets) in order to package our bundle files inside the binary. In order to do this you can build an executable using the below: 66 | 67 | ```shell 68 | deno run --unstable --allow-read --allow-write --allow-net leaf.ts 69 | ``` 70 | 71 | ## Docker and Compose 72 | 73 | You can run the deployment using `docker-compose up` **BUT** you need to build 74 | the phlappjack react image first using `docker build -t phlappjack .` 75 | 76 | ## Mongo 77 | 78 | There is now a .env file to support connecting to Mongo. Please add a .env file 79 | to the root of the repo and populate the fields, e.g: 80 | 81 | .env 82 | ```shell 83 | MONGO_HOST=cluster0.yybae.mongodb.net 84 | MONGO_DB=phlappjack 85 | MONGO_USER=dbUser 86 | MONGO_PASS=secret123 87 | ``` 88 | 89 | The connection uri is already set up in `server/models/` 90 | 91 | ## Dependencies 92 | 93 | Server Dependencies are stored in `deps.ts` 94 | 95 | Client Dependencies are stored in `package.json` and `node_modules` 96 | 97 | 98 | ## Oak Server 99 | 100 | The backend is running Oak and serving static files from the build directory. 101 | 102 | 103 | ## Frontend component architecture 104 | 105 | To aid development, the application structure looks like this: 106 | 107 | ![image](./assets/phlappjack-dependency.png) 108 | 109 | 110 | ## Questions 111 | For questions & community support, please visit our [Discord Channel](https://discord.gg/phlappjack) or join us on our [twitter](https://twitter.com/phlappjack). 112 | 113 | ## Want to help? 114 | ### Interested in coding 115 | In order to submit improvements to the code, open a PR and wait for it to review. We appreciate you doing this. 116 | ### Not interested in coding 117 | We would love to have you in our community, [please submit an issue](https://github.com/oslabs-beta/phlappjack/issues) to provide information about a bug, feature, or improvement you would like. 118 | 119 | ## Follow us wherever we are going 120 | - Author : Kellen Levy Chris Salisbury Andrew Sheehy Jin Qin 121 | - Website : https://www.phlappjack.com/ 122 | - Twitter : [twitter](https://twitter.com/Phlappjack1) 123 | - Discord : [discord](https://discord.gg/j6EmpEJ5) 124 | -------------------------------------------------------------------------------- /server/controllers/controllers.ts: -------------------------------------------------------------------------------- 1 | //connect Mongo to store user's state 2 | import { MongoClient } from "https://deno.land/x/mongo@v0.22.0/mod.ts"; 3 | const URI = "mongodb://127.0.0.1:27017"; 4 | 5 | // Mongo Connection Init 6 | const client = new MongoClient(); 7 | try { 8 | await client.connect(URI); 9 | console.log("Database successfully connected"); 10 | } catch (err) { 11 | console.log(err); 12 | } 13 | 14 | const db = client.database("quotesApp"); 15 | const quotes = db.collection("quotes"); 16 | 17 | interface Quote { 18 | _id: { $oid: string }; 19 | quote: string; 20 | quoteID: string; 21 | author: string; 22 | } 23 | 24 | // DESC: ADD single quote 25 | // METHOD: POST /api/quote 26 | const addQuote = async ({ 27 | request, 28 | response, 29 | }: { 30 | request: any; 31 | response: any; 32 | }) => { 33 | try { 34 | // If the request has no Body, it will return a 404 35 | if (!request.hasBody) { 36 | response.status = 400; 37 | response.body = { 38 | success: false, 39 | msg: "No Data", 40 | }; 41 | } else { 42 | // Otherwise, it will try to insert 43 | // a quote in the DB and respond with 201 44 | const body = await request.body(); 45 | const quote = await body.value; 46 | await quotes.insertOne(quote); 47 | response.status = 201; 48 | response.body = { 49 | success: true, 50 | data: quote, 51 | }; 52 | } 53 | } catch (err) { 54 | response.body = { 55 | success: false, 56 | msg: err.toString(), 57 | }; 58 | } 59 | }; 60 | 61 | // DESC: GET single quote 62 | // METHOD: GET /api/quote/:id 63 | const getQuote = async ({ 64 | params, 65 | response, 66 | }: { 67 | params: { id: string }; 68 | response: any; 69 | }) => { 70 | // Searches for a particular quote in the DB 71 | const quote = await quotes.findOne({ quoteID: params.id }); 72 | // If found, respond with the quote. If not, respond with a 404 73 | if (quote) { 74 | response.status = 200; 75 | response.body = { 76 | success: true, 77 | data: quote, 78 | }; 79 | } else { 80 | response.status = 404; 81 | response.body = { 82 | success: false, 83 | msg: "No quote found", 84 | }; 85 | } 86 | }; 87 | 88 | // DESC: GET all Quotes 89 | // METHOD GET /api/quote 90 | const getQuotes = async ({ response }: { response: any }) => { 91 | try { 92 | // Find all quotes and convert them into an Array 93 | const allQuotes = await quotes.find({}).toArray(); 94 | console.log(allQuotes); 95 | if (allQuotes) { 96 | response.status = 200; 97 | response.body = { 98 | success: true, 99 | data: allQuotes, 100 | }; 101 | } else { 102 | response.status = 500; 103 | response.body = { 104 | success: false, 105 | msg: "Internal Server Error", 106 | }; 107 | } 108 | } catch (err) { 109 | response.body = { 110 | success: false, 111 | msg: err.toString(), 112 | }; 113 | } 114 | }; 115 | For updating a particular quote, let's build the updateQuote function. 116 | // DESC: UPDATE single quote 117 | // METHOD: PUT /api/quote/:id 118 | const updateQuote = async ({ 119 | params, 120 | request, 121 | response, 122 | }: { 123 | params: { id: string }; 124 | request: any; 125 | response: any; 126 | }) => { 127 | try { 128 | // Search a quote in the DB and update with given values if found 129 | const body = await request.body(); 130 | const inputQuote = await body.value; 131 | await quotes.updateOne( 132 | { quoteID: params.id }, 133 | { $set: { quote: inputQuote.quote, author: inputQuote.author } } 134 | ); 135 | // Respond with the Updated Quote 136 | const updatedQuote = await quotes.findOne({ quoteID: params.id }); 137 | response.status = 200; 138 | response.body = { 139 | success: true, 140 | data: updatedQuote, 141 | }; 142 | } catch (err) { 143 | response.body = { 144 | success: false, 145 | msg: err.toString(), 146 | }; 147 | } 148 | }; 149 | 150 | // DESC: DELETE single quote 151 | // METHOD: DELETE /api/quote/:id 152 | const deleteQuote = async ({ 153 | params, 154 | response, 155 | }: { 156 | params: { id: string }; 157 | request: any; 158 | response: any; 159 | }) => { 160 | try { 161 | // Search for the given quote and drop it from the DB 162 | await quotes.deleteOne({ quoteID: params.id }); 163 | response.status = 201; 164 | response.body = { 165 | success: true, 166 | msg: "Product deleted", 167 | }; 168 | } catch (err) { 169 | response.body = { 170 | success: false, 171 | msg: err.toString(), 172 | }; 173 | } 174 | }; 175 | 176 | export { getQuotes, getQuote, addQuote, updateQuote, deleteQuote }; -------------------------------------------------------------------------------- /client/components/MongoDB/ExistingModels/DBNameInput.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import clsx from 'clsx'; 3 | import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; 4 | import Drawer from '@material-ui/core/Drawer'; 5 | import CssBaseline from '@material-ui/core/CssBaseline'; 6 | import AppBar from '@material-ui/core/AppBar'; 7 | import Toolbar from '@material-ui/core/Toolbar'; 8 | import List from '@material-ui/core/List'; 9 | import Typography from '@material-ui/core/Typography'; 10 | import Divider from '@material-ui/core/Divider'; 11 | import IconButton from '@material-ui/core/IconButton'; 12 | import MenuIcon from '@material-ui/icons/Menu'; 13 | import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'; 14 | import ChevronRightIcon from '@material-ui/icons/ChevronRight'; 15 | import ListItem from '@material-ui/core/ListItem'; 16 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 17 | import ListItemText from '@material-ui/core/ListItemText'; 18 | import InboxIcon from '@material-ui/icons/MoveToInbox'; 19 | import MailIcon from '@material-ui/icons/Mail'; 20 | import Button from '@material-ui/core/Button'; 21 | import SaveIcon from '@material-ui/icons/Save'; 22 | import CloudUploadIcon from '@material-ui/icons/CloudUpload'; 23 | import Paper from '@material-ui/core/Paper'; 24 | import TextField from '@material-ui/core/TextField'; 25 | 26 | const drawerWidth = 240; 27 | 28 | const useStyles = makeStyles((theme: Theme) => 29 | createStyles({ 30 | root: { 31 | display: 'flex', 32 | }, 33 | appBar: { 34 | transition: theme.transitions.create(['margin', 'width'], { 35 | easing: theme.transitions.easing.sharp, 36 | duration: theme.transitions.duration.leavingScreen, 37 | }), 38 | }, 39 | appBarShift: { 40 | width: `calc(100% - ${drawerWidth}px)`, 41 | marginLeft: drawerWidth, 42 | transition: theme.transitions.create(['margin', 'width'], { 43 | easing: theme.transitions.easing.easeOut, 44 | duration: theme.transitions.duration.enteringScreen, 45 | }), 46 | }, 47 | menuButton: { 48 | marginRight: theme.spacing(2), 49 | }, 50 | hide: { 51 | display: 'none', 52 | }, 53 | drawer: { 54 | width: drawerWidth, 55 | flexShrink: 0, 56 | }, 57 | drawerPaper: { 58 | width: drawerWidth, 59 | }, 60 | drawerHeader: { 61 | display: 'flex', 62 | alignItems: 'center', 63 | padding: theme.spacing(0, 1), 64 | // necessary for content to be below app bar 65 | ...theme.mixins.toolbar, 66 | justifyContent: 'flex-end', 67 | }, 68 | content: { 69 | flexGrow: 1, 70 | padding: theme.spacing(3), 71 | transition: theme.transitions.create('margin', { 72 | easing: theme.transitions.easing.sharp, 73 | duration: theme.transitions.duration.leavingScreen, 74 | }), 75 | marginLeft: -drawerWidth, 76 | }, 77 | contentShift: { 78 | transition: theme.transitions.create('margin', { 79 | easing: theme.transitions.easing.easeOut, 80 | duration: theme.transitions.duration.enteringScreen, 81 | }), 82 | marginLeft: 0, 83 | }, 84 | button: { 85 | margin: theme.spacing(1), 86 | }, 87 | }), 88 | ); 89 | 90 | export default function DBNameInput(props){ 91 | const classes = useStyles(); 92 | 93 | const handleKeyPress = (e) => { 94 | if(e.keyCode === 13){ 95 | e.preventDefault(); 96 | const collectionInputEle = e.target as HTMLElement; 97 | props.setDBBeingModified(collectionInputEle.value) 98 | if (!props.dbInputDisplay[collectionInputEle.value]){ 99 | const newDBInputDisplay = props.dbInputDisplay; 100 | newDBInputDisplay[collectionInputEle.value] = []; 101 | props.setDBInputDisplay(newDBInputDisplay); 102 | const newEndPoint: string = String('/' + collectionInputEle.value); 103 | const newEndPoints = props.endPoints; 104 | newEndPoints[newEndPoint] = []; 105 | props.setEndPoints(newEndPoints); 106 | 107 | const newIdEndPoint: string = String('/' + collectionInputEle.value + ':id'); 108 | newEndPoints[newIdEndPoint] = []; 109 | props.setEndPoints(newEndPoints); 110 | //Modify newDBToggleState 111 | const newDBToggleState = new Array(Object.keys(props.dbInputDisplay).length).fill(true).map((item, idx) => true); 112 | props.setDBToggles(newDBToggleState); 113 | } 114 | collectionInputEle.value = ''; 115 | } 116 | } 117 | 118 | return ( 119 |
120 |
121 | handleKeyPress(e)} 128 | /> 129 | 130 |
131 | ); 132 | } 133 | -------------------------------------------------------------------------------- /client/components/Home/LoadExistingApplicationsDisplay.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect, useRef } = React; 3 | 4 | import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; 5 | import List from '@material-ui/core/List'; 6 | import Divider from '@material-ui/core/Divider'; 7 | import ListItem from '@material-ui/core/ListItem'; 8 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 9 | import ListItemText from '@material-ui/core/ListItemText'; 10 | import Paper from '@material-ui/core/Paper'; 11 | import TextField from '@material-ui/core/TextField'; 12 | 13 | const useStyles = makeStyles((theme: Theme) => 14 | createStyles({ 15 | listItemText:{ 16 | fontSize:'1.5em', 17 | textIndent:'1em', 18 | textAlign:'center' 19 | } 20 | }), 21 | ); 22 | 23 | export default function DBInputFieldDisplay(props){ 24 | 25 | const classes = useStyles(); 26 | 27 | const handleClick = (e) =>{ 28 | const selectedApplicationEle = (e.target) as HTMLDivElement; 29 | const applicationIndex: number = selectedApplicationEle.parentNode.parentNode.id.split('_')[1]; 30 | props.setNewApplication(props.applicationsToLoad[applicationIndex]) 31 | const newChildKey: number = props.childKeyForLoadingApplication + 1; 32 | props.setChildKeyForLoadingApplication(newChildKey) 33 | console.log(props.childKeyForLoadingApplication) 34 | } 35 | 36 | const handleMouseDown = (e) =>{ 37 | e.preventDefault(); 38 | let mouseup = false; 39 | document.addEventListener("mouseup", 40 | () => { 41 | mouseup = true; 42 | }, {once:true} 43 | ); 44 | const selectedApplicationEle = (e.target) as HTMLDivElement; 45 | let redPercentage: number = 0; 46 | const intervalID = setInterval(() => { 47 | redPercentage += 1; 48 | if(redPercentage === 100){ 49 | clearInterval(intervalID); 50 | const indexToRemove: number = selectedApplicationEle.parentNode.parentNode.id.split('_')[1]; 51 | const newApplicationsToLoad = props.applicationsToLoad; 52 | window.localStorage.removeItem(newApplicationsToLoad[indexToRemove]); 53 | newApplicationsToLoad.splice(indexToRemove,1); 54 | props.setApplicationsToLoad(newApplicationsToLoad); 55 | //Force parent component to update. 56 | const newChildKey: number = props.childKeyForLoadingApplication + 1; 57 | props.setChildKeyForLoadingApplication(newChildKey) 58 | }else if(mouseup === false && redPercentage > 20){ 59 | selectedApplicationEle.style.background = `linear-gradient(90deg, #ffffff ${100 - redPercentage}%, #ff0000 ${redPercentage}%)` 60 | } else if (mouseup === true){ 61 | selectedApplicationEle.style.background = `transparent` 62 | clearInterval(intervalID); 63 | } 64 | },25) 65 | } 66 | 67 | 68 | const useClickOutside = (ref) => { 69 | const handleClick = e => { 70 | if (ref.current && !ref.current.contains(e.target)) { 71 | const newLoadState = new Array(props.applicationsToLoad).fill(true).map((item, idx) => true); 72 | props.setLoadToggles(newLoadState); 73 | } 74 | }; 75 | useEffect(() => { 76 | document.addEventListener('click', handleClick); 77 | return () => { 78 | document.removeEventListener('click', handleClick); 79 | }; 80 | }); 81 | }; 82 | 83 | const editField = useRef(); 84 | 85 | useClickOutside(editField); 86 | 87 | const handleChange = (e) =>{ 88 | props.setEditLoadTextFieldValue(e.target.value) 89 | const loadIndex: number = e.target.id.split('_')[1]; 90 | const newApplicationsToLoad = props.applicationsToLoad; 91 | //Store Props of the old application name. 92 | const oldApplicationProps = window.localStorage.getItem(newApplicationsToLoad[loadIndex]); 93 | //Remove previous application. 94 | window.localStorage.removeItem(newApplicationsToLoad[loadIndex]); 95 | //Store new application name with props into local storage. 96 | window.localStorage.setItem(e.target.value,oldApplicationProps) 97 | newApplicationsToLoad[loadIndex] = e.target.value; 98 | props.setApplicationsToLoad(newApplicationsToLoad); 99 | } 100 | 101 | let applicationsToLoadNames = [] 102 | if(props.applicationsToLoad){ 103 | applicationsToLoadNames = props.applicationsToLoad; 104 | } 105 | applicationsToLoadNames.sort(); 106 | 107 | return ( 108 |
109 | 110 | {applicationsToLoadNames.map((text, index) => ( 111 | props.loadToggles[index] ? ( 112 | handleMouseDown(e)} 116 | onClick = {(e) => handleClick(e)} 117 | onDoubleClick = {() =>{ 118 | let newLoadToggleState = props.loadToggles; 119 | newLoadToggleState[index] = false; 120 | props.setLoadToggles(newLoadToggleState); 121 | props.setEditLoadTextFieldValue(props.applicationsToLoad[index]) 122 | //Force parent component to update. 123 | const newChildKey: number = props.childKeyForLoadingApplication + 1; 124 | props.setChildKeyForLoadingApplication(newChildKey) 125 | }} 126 | > 127 | 131 | 132 | ):( 133 |
134 | 145 |
146 | ) 147 | ))} 148 |
149 |
150 | ); 151 | } 152 | -------------------------------------------------------------------------------- /client/components/Deno/Deno.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStyles, Grid, Paper} from '@material-ui/core'; 3 | import { makeStyles, Theme } from '@material-ui/core/styles'; 4 | import Button from '@material-ui/core/Button'; 5 | import clsx from 'clsx'; 6 | import PublishRoundedIcon from '@material-ui/icons/PublishRounded'; 7 | import GetAppIcon from '@material-ui/icons/GetApp'; 8 | import CheckIcon from '@material-ui/icons/Check'; 9 | import CircularProgress from '@material-ui/core/CircularProgress'; 10 | import { green } from '@material-ui/core/colors'; 11 | import TextField from '@material-ui/core/TextField'; 12 | 13 | 14 | 15 | const useStyles = makeStyles((theme:Theme) => 16 | createStyles({ 17 | root: { 18 | display: 'flex', 19 | alignItems: 'center', 20 | }, 21 | form: { 22 | flex: 1, 23 | }, 24 | wrapper: { 25 | margin: theme.spacing(1), 26 | position: 'relative', 27 | display: 'flex', 28 | justifyContent: 'center' 29 | }, 30 | paper: { 31 | margin: theme.spacing(1), 32 | padding: theme.spacing(6), 33 | flex: 1 34 | }, 35 | buttonSuccess: { 36 | backgroundColor: green[500], 37 | '&:hover': { 38 | backgroundColor: green[700], 39 | }, 40 | }, 41 | buttonProgress: { 42 | color: green[500], 43 | position: 'absolute', 44 | top: '50%', 45 | left: '50%', 46 | marginTop: -12, 47 | marginLeft: -12, 48 | }, 49 | }), 50 | ); 51 | 52 | 53 | export default function Deno(props){ 54 | const classes = useStyles(); 55 | const [gitLoading, setGitLoading] = React.useState(false); 56 | const [gitSuccess, setGitSuccess] = React.useState(false); 57 | const [deployLoading, setDeployLoading] = React.useState(false); 58 | const [deploySuccess, setDeploySuccess] = React.useState(false); 59 | const timer = React.useRef(); 60 | 61 | const [name, setName] = React.useState(''); 62 | const handleChange = (event: React.ChangeEvent) => { 63 | setName(event.target.value); 64 | }; 65 | 66 | const gitButtonClassname = clsx({ 67 | [classes.buttonSuccess]: gitSuccess, 68 | }); 69 | const deployButtonClassname = clsx({ 70 | [classes.buttonSuccess]: deploySuccess, 71 | }); 72 | 73 | React.useEffect(() => { 74 | return () => { 75 | clearTimeout(timer.current); 76 | }; 77 | }, []); 78 | 79 | const handleButtonClickGit = () => { 80 | console.log(props.newApplication) 81 | console.log(name) 82 | fetch(`/gitclone/${props.newApplication}`, { 83 | method: "POST", 84 | body: name 85 | }) 86 | if (!gitLoading) { 87 | setGitSuccess(false); 88 | setGitLoading(true); 89 | timer.current = window.setTimeout(() => { 90 | setGitSuccess(true); 91 | setGitLoading(false); 92 | }, 20000); 93 | } 94 | }; 95 | const handleButtonClickDeploy = () => { 96 | console.log(props.newApplication) 97 | fetch(`/gitpush/${props.newApplication}`, { 98 | method: "POST", 99 | body: name 100 | }) 101 | if (!deployLoading) { 102 | setDeploySuccess(false); 103 | setDeployLoading(true); 104 | timer.current = window.setTimeout(() => { 105 | setDeploySuccess(true); 106 | setDeployLoading(false); 107 | }, 5000); 108 | } 109 | }; 110 | 111 | return ( 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |
120 | 131 | 132 |
133 | 145 | {gitLoading && } 146 |
147 |
148 | 160 | {deployLoading && } 161 |
162 |
163 | 164 | 165 |
166 |
167 |
168 | ) 169 | } -------------------------------------------------------------------------------- /client/components/MongoDB/ExistingModels/ExistingModelsDisplay.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState,useEffect,useRef } = React; 3 | import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; 4 | import Divider from '@material-ui/core/Divider'; 5 | import List from '@material-ui/core/List'; 6 | import ListItem from '@material-ui/core/ListItem'; 7 | import ListItemText from '@material-ui/core/ListItemText'; 8 | import Paper from '@material-ui/core/Paper'; 9 | import TextField from '@material-ui/core/TextField'; 10 | 11 | const useStyles = makeStyles((theme: Theme) => 12 | createStyles({ 13 | listItemText:{ 14 | fontSize:'2em', 15 | textAlign:'left' 16 | } 17 | }), 18 | ); 19 | 20 | export default function ExistingModelsDisplay(props){ 21 | 22 | const classes = useStyles(); 23 | 24 | const [dbBeingEdited, setdbBeingEdited ] = useState(); 25 | 26 | const handleSelection = (e) =>{ 27 | e.preventDefault(); 28 | const selectedDBName: string = e.target.innerText; 29 | props.setDBBeingModified(selectedDBName); 30 | } 31 | 32 | const handleMouseDown = (e) =>{ 33 | e.preventDefault(); 34 | let mouseup = false; 35 | document.addEventListener("mouseup", 36 | () => { 37 | mouseup = true; 38 | }, {once:true} 39 | ); 40 | const selectedKeyValueEle = (e.target) as HTMLDivElement; 41 | let redPercentage: number = 0; 42 | const intervalID = setInterval(() => { 43 | redPercentage += 1; 44 | if(redPercentage === 100){ 45 | clearInterval(intervalID); 46 | const indexToRemove: string = selectedKeyValueEle.parentNode.parentNode.id.split('_')[1]; 47 | const newDBInputDisplay = props.dbInputDisplay; 48 | 49 | //Remove relevant endpoints from props.endPoints. 50 | const newEndPoints = props.endPoints; 51 | let newEndPointNames = Object.keys(newEndPoints); 52 | newEndPointNames.filter((val) =>{ 53 | if(val.indexOf(indexToRemove) > -1) delete newEndPoints[val] 54 | }) 55 | props.setEndPoints(newEndPoints); 56 | 57 | delete newDBInputDisplay[indexToRemove]; 58 | props.setDBInputDisplay(newDBInputDisplay); 59 | props.setDBBeingModified('DB Input Field'); 60 | }else if(mouseup === false && redPercentage > 10){ 61 | selectedKeyValueEle.style.background = `linear-gradient(90deg, #ffffff ${100 - redPercentage}%, #ff0000 ${redPercentage}%)` 62 | } else if (mouseup === true){ 63 | selectedKeyValueEle.style.background = `transparent` 64 | clearInterval(intervalID); 65 | } 66 | }, 25) 67 | 68 | } 69 | 70 | const useClickOutside = (ref) => { 71 | const handleClick = e => { 72 | 73 | if (ref.current && !ref.current.contains(e.target)) { 74 | const newDBNameState = new Array(Object.keys(props.dbInputDisplay).length).fill(true).map((item, idx) => true); 75 | props.setDBToggles(newDBNameState); 76 | const dbName: string = dbBeingEdited; 77 | const newDBInputDisplay = props.dbInputDisplay; 78 | const modifiedDBInputFields = newDBInputDisplay[dbName]; 79 | delete newDBInputDisplay[dbName]; 80 | newDBInputDisplay[props.editDBTextFieldValue] = modifiedDBInputFields; 81 | props.setDBInputDisplay(newDBInputDisplay); 82 | 83 | //Remove relevant endpoints from props.endPoints. 84 | const newEndPoints = props.endPoints; 85 | let newEndPointNames = Object.keys(newEndPoints); 86 | newEndPointNames.forEach((val) =>{ 87 | if(val.indexOf(dbName) > -1 && ( val.length === dbName.length + 1 ) || (val.indexOf(dbName + ':') > -1)){ 88 | let newValue: string = val.replace(dbName,props.editDBTextFieldValue); 89 | delete newEndPoints[val]; 90 | newEndPoints[newValue] = []; 91 | } 92 | }) 93 | props.setEndPoints(newEndPoints) 94 | //Rename relevant routes that included the previous names. 95 | const newRoutes = props.routes; 96 | newRoutes.forEach((route,index) =>{ 97 | if(route.indexOf(dbName) > -1) newRoutes[index] = route.replace(dbName,props.editDBTextFieldValue); 98 | }) 99 | //Force parent component to update. 100 | const newChildKey0: number = props.childKey0 + 1; 101 | props.setChildKey0(newChildKey0) 102 | } 103 | }; 104 | useEffect(() => { 105 | document.addEventListener('click', handleClick); 106 | return () => { 107 | document.removeEventListener('click', handleClick); 108 | }; 109 | }); 110 | }; 111 | 112 | const editField = useRef(); 113 | 114 | useClickOutside(editField); 115 | 116 | const handleChange = (e) =>{ 117 | props.setEditDBTextFieldValue(e.target.value); 118 | setdbBeingEdited(e.target.id); 119 | } 120 | 121 | let dbNames = []; 122 | if(Object.keys(props.dbInputDisplay).length){ 123 | dbNames = Object.keys(props.dbInputDisplay); 124 | } 125 | dbNames.sort(); 126 | 127 | return ( 128 |
129 | 130 | 131 | 132 | {dbNames.map((text, index) => ( 133 | props.dbToggles[index] ? ( 134 | 138 | handleSelection(e)} 142 | onMouseDown = {(e) => handleMouseDown(e)} 143 | onDoubleClick = {() =>{ 144 | let newDBToggleState = props.dbToggles; 145 | newDBToggleState[index] = false; 146 | props.setDBToggles(newDBToggleState); 147 | props.setEditDBTextFieldValue(text) 148 | //Force parent component to update. 149 | const newChildKey0: number = props.childKey0 + 1; 150 | props.setChildKey0(newChildKey0) 151 | }} 152 | /> 153 | 154 | ) : ( 155 |
156 | 168 |
169 | ) 170 | ))} 171 |
172 |
173 |
174 | ); 175 | } 176 | -------------------------------------------------------------------------------- /client/components/Oak/RoutingInformation/Middleware.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useState, useEffect } = React; 3 | 4 | import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; 5 | import Paper from '@material-ui/core/Paper'; 6 | import InputLabel from '@material-ui/core/InputLabel'; 7 | import MenuItem from '@material-ui/core/MenuItem'; 8 | import FormControl from '@material-ui/core/FormControl'; 9 | import Select from '@material-ui/core/Select'; 10 | import TextField from '@material-ui/core/TextField'; 11 | import IconButton from "@material-ui/core/IconButton"; 12 | import InputAdornment from "@material-ui/core/InputAdornment"; 13 | import SearchIcon from "@material-ui/icons/Search"; 14 | 15 | import ExistingModelsDisplay from './ExistingModelsDisplay' 16 | import DBNameInput from './DBNameInput'; 17 | import { TextFieldsOutlined } from '@material-ui/icons'; 18 | 19 | const styles = { 20 | container: { 21 | display: 'flex', 22 | flexWrap: 'wrap', 23 | }, 24 | textField: { 25 | width: 300, 26 | margin: 100, 27 | }, 28 | //style for font size 29 | resize:{ 30 | fontSize:50 31 | }, 32 | } 33 | 34 | export default function Middleware(props){ 35 | 36 | const handleChange = (e) =>{ 37 | props.setMiddleWareTemp(e.target.value) 38 | } 39 | 40 | const handKeyDown = (e) =>{ 41 | if(e.keyCode === 9){ 42 | e.preventDefault(); 43 | document.execCommand('insertText', false, "\t"); 44 | } 45 | } 46 | 47 | useEffect(() =>{ 48 | 49 | const dbNames = Object.keys(props.dbInputDisplay); 50 | const endPointName: string = props.selectedEndPoint; 51 | let dbName: string; 52 | if(props.selectedEndPoint.includes(':')){ 53 | dbName = endPointName.split(':')[0]; 54 | dbName = dbName.split('/')[1]; 55 | } else { 56 | dbName = endPointName.split('/')[1]; 57 | } 58 | 59 | if(!props.selectedEndPoint || dbName && !props.dbInputDisplay[dbName].length) return; 60 | 61 | let newMiddleWareTemp: string; 62 | 63 | if( props.selectedEndPoint !== 'Routing Information' && props.resMethod && dbNames.indexOf(dbName) > -1){ 64 | //Middleware template for findAll (works) 65 | if(props.resMethod == 'get' && !props.selectedEndPoint.includes(':')){ 66 | const currModel: string = props.selectedEndPoint.split('/')[1]; 67 | const currModelInput: string = props.dbInputDisplay[currModel][0].toString().split(':')[0]; 68 | newMiddleWareTemp =`.${props.resMethod}('${props.selectedEndPoint}', async (ctx) => { 69 | \tconst ${currModel}_findAll = await ${currModel}.find( 70 | \t\t{_id: { $ne: null }}, 71 | \t\t{noCursorTimeout: false} 72 | \t).toArray(); 73 | \tctx.response.body = ${currModel}_findAll; 74 | })` 75 | //Middleware template for find based on specified input field. (works) 76 | } else if (props.resMethod == 'get' && props.selectedEndPoint.includes(':id')){ 77 | const currModel: string = props.selectedEndPoint.split('/')[1].split(':')[0]; 78 | newMiddleWareTemp =`.${props.resMethod}('${props.selectedEndPoint}', async (ctx) => { 79 | \tconst { id } = helpers.getQuery(ctx, {mergeParams: true }); 80 | \tif( id ){ 81 | \t\tconst ${currModel}_findOne = await ${currModel}.findOne( 82 | \t\t\t{_id: new Bson.ObjectId(String(id))}, 83 | \t\t\t{ noCursorTimeout:false } 84 | \t\t); 85 | \t\tctx.response.body = ${currModel}_findOne; 86 | \t} 87 | })` 88 | //Middleware template for insert one. (works) 89 | } else if (props.resMethod == 'post' && !props.selectedEndPoint.includes(':')){ 90 | const currModel: string = props.selectedEndPoint.split('/')[1]; 91 | const currModelInput: string = props.dbInputDisplay[currModel][0].toString().split(':')[0]; 92 | newMiddleWareTemp =`.${props.resMethod}('${props.selectedEndPoint}', async (ctx) => { 93 | \tconst body = await ctx.request.body() 94 | \tconst value = await body.value; 95 | \tconst ${currModel}_insertOne = await ${currModel}.insert(value); 96 | \tctx.response.body = ${currModel}_insertOne; 97 | })` 98 | //Middleware template for update one. (works) 99 | } else if (props.resMethod == 'patch' && props.selectedEndPoint.includes(':')){ 100 | const currModel: string = props.selectedEndPoint.split('/')[1].split(':')[0]; 101 | const currModelInput: string = props.selectedEndPoint.split(':')[1]; 102 | newMiddleWareTemp =`.${props.resMethod}('${props.selectedEndPoint}', async (ctx) => { 103 | \tconst { id, ${currModelInput} } = helpers.getQuery(ctx, {mergeParams: true }); 104 | \tif(id && ${currModelInput}){ 105 | \t\tconst ${currModel}_updateOne = await ${currModel}.updateOne( 106 | \t\t\t{_id:new Bson.ObjectId(id)}, 107 | \t\t\t{$set:{${currModelInput}:${currModelInput}}} 108 | \t\t); 109 | \tctx.response.body = ${currModel}_updateOne; 110 | } 111 | })` 112 | //Middleware template for update many. (works) 113 | } else if(props.resMethod == 'put' && props.selectedEndPoint.includes(':')){ 114 | const currModel: string = props.selectedEndPoint.split('/')[1].split(':')[0]; 115 | const currModelInput: string = props.selectedEndPoint.split(':')[1]; 116 | newMiddleWareTemp =`.${props.resMethod}('${props.selectedEndPoint}', async (ctx) => { 117 | const { ${currModelInput} } = helpers.getQuery(ctx, {mergeParams: true }); 118 | if(${currModelInput}){ 119 | \tconst ${currModel}_updateMany = await ${currModel}.updateMany( 120 | \t\t{${currModelInput}:{ $ne: null}}, 121 | \t\t{$set:{${currModelInput}: ${currModelInput}}} 122 | \t); 123 | \tctx.response.body = ${currModel}_updateMany; 124 | } 125 | })` 126 | //Middleware template for delete one. 127 | } else if(props.resMethod == 'delete' && props.selectedEndPoint.includes(':id')){ 128 | const currModel: string = props.selectedEndPoint.split('/')[1].split(':')[0]; 129 | const currModelInput: string = props.selectedEndPoint.split(':')[1]; 130 | newMiddleWareTemp =`.${props.resMethod}('${props.selectedEndPoint}', async (ctx) => { 131 | const { id } = helpers.getQuery(ctx, {mergeParams: true }); 132 | if( id ){ 133 | \tconst ${currModel}_deleteOne = await ${currModel}.deleteOne({ 134 | \t\t_id: new Bson.ObjectId(id) 135 | \t}); 136 | \tctx.response.body = ${currModel}_deleteOne; 137 | } 138 | })` 139 | //Middleware template for delete many. 140 | } else if(props.resMethod == 'delete' && props.selectedEndPoint.includes(':')){ 141 | const currModel: string = props.selectedEndPoint.split('/')[1].split(':')[0]; 142 | const currModelInput: string = props.selectedEndPoint.split(':')[1]; 143 | newMiddleWareTemp =`.${props.resMethod}('${props.selectedEndPoint}', async (ctx) => { 144 | const { ${currModelInput} } = helpers.getQuery(ctx, {mergeParams: true }); 145 | if( ${currModelInput} ){ 146 | \tconst ${currModel}_deleteMany = await ${currModel}.deleteMany({ 147 | \t\t${currModelInput}:ctx.params.${currModelInput} 148 | \t}); 149 | \tctx.response.body = ${currModel}_deleteMany; 150 | } 151 | })` 152 | }else { 153 | 154 | newMiddleWareTemp = ''; 155 | 156 | } 157 | 158 | } else if( props.selectedEndPoint !== 'Routing Information' && props.resMethod ) { 159 | 160 | newMiddleWareTemp =`.${props.resMethod}('${props.selectedEndPoint}', async (ctx) => { 161 | 162 | }) 163 | ` 164 | } 165 | props.setMiddleWareTemp(newMiddleWareTemp) 166 | },[props.resMethod, props.selectedEndPoint]) 167 | 168 | // const generateLineNumbers = () => { 169 | // let lineNumberStr = ''; 170 | // for (let i = 1; i <= 100; i ++){ 171 | // lineNumberStr += i + '\n' 172 | // } 173 | // return lineNumberStr; 174 | // } 175 | 176 | return ( 177 | 181 | Kg 192 | }} 193 | onKeyDown = {handKeyDown} 194 | onChange = {handleChange} 195 | /> 196 | 197 | ); 198 | } -------------------------------------------------------------------------------- /client/components/MyDrawer.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import { IconButton, createStyles, Typography} from '@material-ui/core'; 3 | import { makeStyles, useTheme, Theme } from '@material-ui/core/styles' 4 | import {FaHome} from 'react-icons/fa'; 5 | import {DiMongodb} from 'react-icons/di'; 6 | import {GiSquirrel} from 'react-icons/gi'; 7 | import {SiDeno} from 'react-icons/si'; 8 | import {FaDocker} from 'react-icons/fa'; 9 | import { Route } from 'react-router'; 10 | import { Link as RouterLink, LinkProps as RouterLinkProps } from 'react-router-dom'; 11 | import { Omit } from '@material-ui/types'; 12 | import Divider from '@material-ui/core/Divider'; 13 | import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'; 14 | import ChevronRightIcon from '@material-ui/icons/ChevronRight'; 15 | import ListItem from '@material-ui/core/ListItem'; 16 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 17 | import ListItemText from '@material-ui/core/ListItemText'; 18 | import Drawer from '@material-ui/core/Drawer'; 19 | import List from '@material-ui/core/List'; 20 | import SaveIcon from '@material-ui/icons/Save'; 21 | import Button from '@material-ui/core/Button'; 22 | 23 | const drawerWidth = 240; 24 | 25 | const useStyles = makeStyles((theme: Theme) => 26 | createStyles({ 27 | root: { 28 | display: 'flex', 29 | }, 30 | appBar: { 31 | transition: theme.transitions.create(['margin', 'width'], { 32 | easing: theme.transitions.easing.sharp, 33 | duration: theme.transitions.duration.leavingScreen, 34 | }), 35 | }, 36 | appBarShift: { 37 | width: `calc(100% - ${drawerWidth}px)`, 38 | marginLeft: drawerWidth, 39 | transition: theme.transitions.create(['margin', 'width'], { 40 | easing: theme.transitions.easing.easeOut, 41 | duration: theme.transitions.duration.enteringScreen, 42 | }), 43 | }, 44 | menuButton: { 45 | marginRight: theme.spacing(2), 46 | }, 47 | hide: { 48 | display: 'none', 49 | }, 50 | drawer: { 51 | width: drawerWidth, 52 | flexShrink: 0, 53 | }, 54 | drawerPaper: { 55 | width: drawerWidth, 56 | }, 57 | drawerHeader: { 58 | display: 'flex', 59 | alignItems: 'center', 60 | justifyContent: 'space-between', 61 | padding: theme.spacing(0, 1), 62 | // necessary for content to be below app bar 63 | ...theme.mixins.toolbar, 64 | // justifyContent: 'space-evenly', 65 | }, 66 | drawerHeaderText: { 67 | paddingLeft: theme.spacing(8), 68 | }, 69 | content: { 70 | flexGrow: 1, 71 | padding: theme.spacing(3), 72 | transition: theme.transitions.create('margin', { 73 | easing: theme.transitions.easing.sharp, 74 | duration: theme.transitions.duration.leavingScreen, 75 | }), 76 | marginLeft: -drawerWidth, 77 | }, 78 | contentShift: { 79 | transition: theme.transitions.create('margin', { 80 | easing: theme.transitions.easing.easeOut, 81 | duration: theme.transitions.duration.enteringScreen, 82 | }), 83 | marginLeft: 0, 84 | }, 85 | button: { 86 | margin: theme.spacing(1), 87 | }, 88 | }), 89 | ); 90 | 91 | interface ListItemLinkProps { 92 | icon?: React.ReactElement; 93 | primary: string; 94 | to: string; 95 | } 96 | 97 | 98 | function ListItemLink(props: ListItemLinkProps) { 99 | const { icon, primary, to } = props; 100 | 101 | const renderLink = React.useMemo( 102 | () => 103 | React.forwardRef>((itemProps, ref) => ( 104 | 105 | )), 106 | [to], 107 | ); 108 | 109 | return ( 110 |
  • 111 | 112 | {icon ? {icon} : null} 113 | 114 | 115 |
  • 116 | ); 117 | } 118 | 119 | 120 | 121 | export default function MyDrawer(props){ 122 | const classes = useStyles(); 123 | const theme = useTheme(); 124 | 125 | const configureApp = () =>{ 126 | 127 | fetch('/export', { 128 | method: 'POST', 129 | headers: { 130 | 'Content-Type': 'application/json', 131 | }, 132 | body: JSON.stringify(props) 133 | }) 134 | .then(response => response.json()) 135 | .then(data => { 136 | console.log(data); 137 | }) 138 | .catch((error) => { 139 | console.error('error'); 140 | }); 141 | 142 | } 143 | 144 | const saveApplication = () => { 145 | const currApplicationName: string = props.newApplication; 146 | let propsToSave = {}; 147 | const propKeys = Object.keys(props); 148 | for (let i = 0; i < propKeys.length; i++){ 149 | let propValue = props[propKeys[i]]; 150 | let propKey = String('_' + propKeys[i]) 151 | propsToSave[propKey] = propValue; 152 | } 153 | 154 | window.localStorage.setItem(currApplicationName, JSON.stringify(propsToSave)) 155 | const newKey: number = props.childKey + 1 156 | props.setChildKey(newKey) 157 | } 158 | 159 | const [sectionSelected, setSectionSelected] = useState({ 160 | home: false, 161 | mongo: false, 162 | oak: false, 163 | deno: false, 164 | docker: false 165 | }) 166 | 167 | const handleSelect = (selection) => { 168 | 169 | const resetState = { 170 | home: false, 171 | mongo: false, 172 | oak: false, 173 | deno: false, 174 | docker: false 175 | } 176 | 177 | const update = { resetState, [selection]: true } 178 | setSectionSelected(update) 179 | } 180 | 181 | return ( 182 |
    183 | 184 | 193 |
    194 |
    195 | Options 196 |
    197 | 198 | {theme.direction === 'ltr' ? : } 199 | 200 |
    201 | 202 | 203 | handleSelect('home')}> 204 | } /> 205 | 206 | handleSelect('deno')}> 207 | } /> 208 | 209 | handleSelect('mongo')}> 210 | } /> 211 | 212 | handleSelect('oak')}> 213 | }/> 214 | 215 | handleSelect('docker')}> 216 | } /> 217 | 218 | 219 | 220 | 230 | 239 | 240 |
    241 |
    242 | ) 243 | } 244 | -------------------------------------------------------------------------------- /client/components/Oak/RoutingInformation/RouteExcecutionOrderDisplay.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | const { useEffect, useState, useRef } = React; 3 | import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd' 4 | 5 | import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; 6 | import List from '@material-ui/core/List'; 7 | import Divider from '@material-ui/core/Divider'; 8 | import ListItem from '@material-ui/core/ListItem'; 9 | import ListItemText from '@material-ui/core/ListItemText'; 10 | import Paper from '@material-ui/core/Paper'; 11 | import TextField from '@material-ui/core/TextField'; 12 | 13 | const drawerWidth = 240; 14 | 15 | const useStyles = makeStyles((theme: Theme) => 16 | createStyles({ 17 | listItemRouter:{ 18 | fontSize:'1em', 19 | textAlign:'left', 20 | }, 21 | listItemText:{ 22 | fontSize:'1em', 23 | textAlign:'left', 24 | marginLeft:'1.5em', 25 | // marginTop:'-1.0em', 26 | // marginBottom:'-1.0em', 27 | whiteSpace: 'pre-wrap', 28 | } 29 | }), 30 | ); 31 | 32 | export default function RouteExecutionOrderDisplay(props){ 33 | 34 | const classes = useStyles(); 35 | 36 | const handKeyDown = (e) =>{ 37 | if(e.keyCode === 9){ 38 | e.preventDefault(); 39 | document.execCommand('insertText', false, "\t"); 40 | } 41 | } 42 | 43 | 44 | 45 | const handleMouseDown = (e) =>{ 46 | e.preventDefault(); 47 | let mouseup = false; 48 | document.addEventListener("mouseup", 49 | () => { 50 | mouseup = true; 51 | }, {once:true} 52 | ); 53 | const selectedRouteEle = (e.target) as HTMLDivElement; 54 | const backgroundColorDiv = selectedRouteEle.parentNode.parentNode.parentNode as HTMLDivElement; 55 | let redPercentage: number = 0; 56 | const intervalID = setInterval(() => { 57 | redPercentage += 1; 58 | if(redPercentage === 100){ 59 | clearInterval(intervalID); 60 | const indexToRemove: number = selectedRouteEle.parentNode.parentNode.id.split('_')[1]; 61 | console.log(indexToRemove) 62 | const newRoutes: Array = props.routes; 63 | newRoutes.splice(indexToRemove, 1) 64 | props.setRoutes(newRoutes); 65 | //Force parent component to update. 66 | const newChildKey: number = Math.floor(Math.random() * 100000); 67 | props.setChildKey(newChildKey); 68 | }else if(mouseup === false && redPercentage > 10){ 69 | backgroundColorDiv.style.background = `linear-gradient(90deg, #ffffff ${100 - redPercentage}%, #ff0000 ${redPercentage}%)` 70 | } else if (mouseup === true){ 71 | backgroundColorDiv.style.background = `transparent` 72 | clearInterval(intervalID); 73 | } 74 | }, 25) 75 | 76 | } 77 | 78 | function handleOnDragEnd(result){ 79 | const items = Array.from(props.routes); 80 | const [reOrderdRoute] = items.splice(result.source.index,1) 81 | items.splice(result.destination.index, 0, reOrderdRoute); 82 | props.setRoutes(items) 83 | } 84 | 85 | const useClickOutside = (ref) => { 86 | const handleClick = e => { 87 | if (ref.current && !ref.current.contains(e.target)) { 88 | const newToggleState = new Array(props.routes.length).fill(true).map((item, idx) => true); 89 | props.setRouteToggles(newToggleState); 90 | } 91 | }; 92 | useEffect(() => { 93 | document.addEventListener('click', handleClick); 94 | return () => { 95 | document.removeEventListener('click', handleClick); 96 | }; 97 | }); 98 | }; 99 | 100 | const editField = useRef(); 101 | 102 | useClickOutside(editField); 103 | 104 | const handleChange = (e) =>{ 105 | props.setEditRouteTextFieldValue(e.target.value) 106 | const routeIndex: number = e.target.id; 107 | const newRoutes = props.routes; 108 | newRoutes[routeIndex] = e.target.value; 109 | props.setRoutes(newRoutes); 110 | } 111 | 112 | 113 | let routerDisplay = []; 114 | if(props.routes){ 115 | for (let i = 0; i < props.routes.length; i++){ 116 | let currRoute = props.routes[i]; 117 | routerDisplay.push(currRoute); 118 | } 119 | } 120 | 121 | 122 | return ( 123 |
    124 | 125 | 126 | 127 | 131 | 135 | 136 | 137 | 138 | 139 | {(provided) => ( 140 | 145 | {routerDisplay.map((text,index) =>( 146 | 151 | {(provided) =>( 152 |
    156 | {props.routeToggles[index] ? ( 157 | handleMouseDown(e)} 166 | onDoubleClick = {() =>{ 167 | let newRoutesToggleState = props.routeToggles; 168 | newRoutesToggleState[index] = false; 169 | props.setRouteToggles(newRoutesToggleState); 170 | props.setEditRouteTextFieldValue(props.routes[index]) 171 | //Force parent component to update. 172 | const newChildKey: number = Math.floor(Math.random() * 100000); 173 | props.setChildKey(newChildKey); 174 | }} 175 | > 176 | 180 | 181 | ) : ( 182 |
    183 | 195 |
    196 | )} 197 |
    198 | )} 199 |
    200 | ))} 201 | {provided.placeholder} 202 |
    203 | ) 204 | } 205 |
    206 |
    207 |
    208 |
    209 | ); 210 | } 211 | -------------------------------------------------------------------------------- /AppConfiguration/appConfigurator.ts: -------------------------------------------------------------------------------- 1 | 2 | import { 3 | prettier, 4 | prettierPlugins 5 | } from "https://denolib.com/denolib/prettier/prettier.ts"; 6 | import { 7 | ensureDir, 8 | ensureFile, 9 | } from "https://deno.land/std/fs/mod.ts"; 10 | 11 | import { importString, setUp, fetchHandler } from "./Imports/ImportsForServer.ts" 12 | import { CRUDFunctionGet, CRUDFunctionGetOne, CRUDFunctionPatch, CRUDFunctionCreateOne, CRUDFunctionDelete } from "./CRUDFunctions.ts" 13 | import { mongooseString } from "./Imports/ImportsForMongo.ts" 14 | import { routerString, exportString } from "./Imports/ImportsForRouter.ts" 15 | 16 | 17 | export const configureApplication = async ( 18 | dir, 19 | applicationName, 20 | mongoHostState, 21 | userNameState, 22 | passWordState, 23 | mongoDBState, 24 | collectionsState, 25 | dockerFile, 26 | dockerComposeFile, 27 | routes 28 | ) => { 29 | 30 | 31 | // here we ensure the file structure for export includes a working directory based on the application name entered at the beginning of the project 32 | // a sever folder subjugated to the top level working directory which will contain: 33 | // a mod.ts file (the sever itself) 34 | // a models file which will contain the shcmeas entered by the user as well as the necessary connections to the Mongo Atlas DB 35 | // a router folder which will contain the proper middleware functionality for CRUD functionality 36 | // sub router files for each schema produced. 37 | 38 | await ensureDir(`${dir}/${applicationName}`) 39 | await ensureDir(`${dir}/${applicationName}/Server`) 40 | await ensureFile(`${dir}/${applicationName}/Server/deps.ts`); 41 | await ensureFile(`${dir}/${applicationName}/Server/mod.ts`) 42 | await ensureDir(`${dir}/${applicationName}/Server/Models`) 43 | await ensureDir(`${dir}/${applicationName}/Server/Routes`) 44 | await ensureFile(`${dir}/${applicationName}/Server/Routes/Router.ts`) 45 | // await ensureDir(`${dir}/${applicationName}/Controllers/`) 46 | await ensureDir(`${dir}/${applicationName}/client`); 47 | await ensureFile(`${dir}/${applicationName}/client/deps.ts`); 48 | await ensureFile(`${dir}/${applicationName}/DockerFile`); 49 | await Deno.writeTextFile(`${dir}/${applicationName}/DockerFile`, dockerFile) 50 | await ensureFile(`${dir}/${applicationName}/docker-compose.yml`); 51 | await Deno.writeTextFile(`${dir}/${applicationName}/docker-compose.yml`, dockerComposeFile) 52 | 53 | 54 | let controllerImportString: string[] = [] 55 | 56 | const createModelsDir = async (obj) => { 57 | // here we iterate through our current schemas and for each of them ensure a file in the newly created user folder /router 58 | const models = Object.keys(obj) 59 | 60 | let mongooseConnectionFileIsCreated: boolean = false 61 | 62 | const prettyConnection = prettier.format(await mongooseString( 63 | mongoHostState, 64 | userNameState, 65 | passWordState, 66 | mongoDBState 67 | ), { 68 | parser: "babel", 69 | plugins: prettierPlugins 70 | }); 71 | 72 | for(let i = 0; i < models.length; i++){ 73 | //here we delcare an empty string whcih will hold our shcema's properties 74 | let schemaValues = ''; 75 | const model = models[i] 76 | 77 | if(mongooseConnectionFileIsCreated){ 78 | await ensureFile(`${dir}/${applicationName}/Server/Models/${model}.ts`) 79 | const write = Deno.writeTextFile(`${dir}/${applicationName}/Server/Models/DBConnection.ts`, prettyConnection) 80 | write.then(() => console.log(`Mongoose Connection File Written to ${dir}/${applicationName}/Server/Models/DBConnection.ts`)) 81 | } else { 82 | await ensureFile(`${dir}/${applicationName}/Server/Models/DBConnection.ts`) 83 | const write = Deno.writeTextFile(`${dir}/${applicationName}/Server/Models/DBConnection.ts`, prettyConnection) 84 | write.then(() => console.log(`Mongoose Connection File Written to ${dir}/${applicationName}/Server/Models/DBConnection.ts`)) 85 | mongooseConnectionFileIsCreated = true 86 | await ensureFile(`${dir}/${applicationName}/Server/Models/${model}.ts`) 87 | } 88 | 89 | //here we need to iterate through each of the mdodels to get their properties 90 | obj[model].forEach((modelInput) =>{ 91 | schemaValues += `${String(modelInput)}`; 92 | }) 93 | 94 | const schemaTemplateString = `import { Bson, Router, helpers } from '../deps.ts'; 95 | import { client } from './DBConnection.ts' 96 | 97 | interface ${model}{ 98 | ${schemaValues} 99 | } 100 | 101 | const db = await client.database("${mongoDBState}")` 102 | 103 | const prettySchema = prettier.format(schemaTemplateString, { 104 | parser: "babel", 105 | plugins: prettierPlugins 106 | }) 107 | 108 | const writeSchema = async() => await Deno.writeTextFile(`${dir}/${applicationName}/Server/Models/${model}.ts`,`${prettySchema}\nconst ${model} = await db.collection<${model}>("${model}")\n 109 | export { ${model} };`); 110 | writeSchema().then(() => console.log(`Schema file for ${model} succesfully wirtten to ${dir}/${applicationName}/Server/Models/${model}.ts`)) 111 | 112 | } 113 | } 114 | 115 | const createControllerFiles = async (obj) => { 116 | //for building base CRUD functions if none are selected from front end...still needs to be implemented as a feature on the fornt end.. 117 | 118 | const models = Object.keys(obj) 119 | 120 | for(let i = 0; i < models.length; i++){ 121 | let controllerFileString = '' 122 | const model = models[i] 123 | 124 | const flatModel = obj[model].flat() 125 | const props = flatModel.reduce((acc, property) => { 126 | property = property.slice(0, property.indexOf(":")) 127 | acc += `${property}, ` 128 | return acc 129 | 130 | }, '') 131 | 132 | //CRUDFunctionGet, CRUDFunctionGetOne, CRUDFunctionPatch, CRUDFunctionCreateOne, CRUDFunctionDelete 133 | const getAllCRUD: string = await CRUDFunctionGet(model) 134 | const getOneCRUD: string = await CRUDFunctionGetOne(model) 135 | const createCRUD: string = await CRUDFunctionCreateOne(model, props) 136 | const updateCRUD: string = await CRUDFunctionPatch(model, props) 137 | const deleteCRUD: string = await CRUDFunctionDelete(model) 138 | 139 | controllerFileString += `${getAllCRUD}; ${getOneCRUD}; ${createCRUD}; ${updateCRUD}; ${deleteCRUD}` 140 | controllerImportString.push(`import {get${model}, getAll${model}, create${model}, update${model}, delete${model}} from "./Controllers/${model}Controller.ts"\n`) 141 | 142 | const prettyController = prettier.format(controllerFileString, { 143 | parser: "babel", 144 | plugins: prettierPlugins 145 | }) 146 | 147 | 148 | await ensureFile(`${dir}/${applicationName}/Controllers/${model}Controller.ts`) 149 | const write = Deno.writeTextFile(`${dir}/${applicationName}/Controllers/${model}Controller.ts`, prettyController) 150 | write.then(() => console.log(`controller File for ${model} Successfully Written to ${dir}/${applicationName}/Controllers/${model}Controller.ts`)) 151 | } 152 | 153 | } 154 | 155 | const createServerFiles = async (obj) => { 156 | let template: string = '' 157 | let routerString: string = '' 158 | const models = Object.keys(obj) 159 | 160 | let routerCount = 0 161 | for(let i = 0; i < models.length; i++){ 162 | const model = models[i] 163 | 164 | if(routerCount < 1){ 165 | const route = `router.get("/${model}", getAll${model}) 166 | .get("/${model}/:id", get${model}) 167 | .post("/${model}", create${model}) 168 | .patch("/${model}/:id", update${model}) 169 | .delete("${model}/:id", delete${model})` 170 | 171 | routerString += `${route}` 172 | routerCount++ 173 | } else { 174 | const route = `.get("/${model}", getAll${model}) 175 | .get("/${model}/:id", get${model}) 176 | .post("/${model}", create${model}) 177 | .patch("/${model}/:id", update${model}) 178 | .delete("${model}/:id", delete${model}) 179 | ` 180 | routerString += `${route}` 181 | routerCount++ 182 | } 183 | } 184 | 185 | template += importString 186 | // controllerImportString.forEach(el => { 187 | // template += el 188 | // }) 189 | template += setUp 190 | template += fetchHandler 191 | 192 | const prettyServer = prettier.format(template, { 193 | parser: "babel", 194 | plugins: prettierPlugins 195 | }) 196 | 197 | const write = Deno.writeTextFile(`${dir}/${applicationName}/Server/mod.ts`, prettyServer) 198 | write.then(() => console.log(`server file succesfully written to ${dir}/${applicationName}/Server/mod.ts`)) 199 | 200 | } 201 | 202 | const createRouteFile = async (routes, collectionsState) => { 203 | const models:Array = Object.keys(collectionsState) 204 | let modelImport: string = '' 205 | for(let i = 0; i < models.length; i++){ 206 | modelImport += `import { ${models[i]} } from '../Models/${models[i]}.ts';\n`; 207 | } 208 | 209 | let routeTemplateStr: string =`import { Bson, Router, helpers } from '../deps.ts'; 210 | ${modelImport} 211 | const router = new Router() 212 | router` 213 | for (let i = 0; i < routes.length; i++){ 214 | routeTemplateStr += '\n\t' + routes[i]; 215 | } 216 | routeTemplateStr += ';'; 217 | routeTemplateStr += `\n\nexport { router };`; 218 | 219 | 220 | let serverDepsTemplateStr: string =`export { Application, Router, helpers } from "https://deno.land/x/oak/mod.ts"; 221 | export { oakCors } from "https://deno.land/x/cors/mod.ts"; 222 | export { MongoClient, Bson } from "https://deno.land/x/mongo/mod.ts"; 223 | `; 224 | 225 | const prettyRouter = prettier.format(routeTemplateStr, { 226 | parser: "babel", 227 | plugins: prettierPlugins 228 | }) 229 | 230 | 231 | const prettyServerDeps = prettier.format(serverDepsTemplateStr, { 232 | parser: "babel", 233 | plugins: prettierPlugins 234 | }) 235 | 236 | 237 | const writeRoute = Deno.writeTextFile(`${dir}/${applicationName}/Server/Routes/Router.ts`, prettyRouter); 238 | writeRoute.then(() => {console.log(`Router file successfully written to ${dir}/${applicationName}/Server/Routes/Router.ts `)}) 239 | 240 | const writeServerDeps = Deno.writeTextFile(`${dir}/${applicationName}/Server/deps.ts`, prettyServerDeps); 241 | writeServerDeps.then(() => {console.log(`Sever Deps File successfully written to ${dir}/${applicationName}/Server/deps.ts`)}) 242 | } 243 | 244 | 245 | await createModelsDir(collectionsState); 246 | //await createControllerFiles(collectionsState); 247 | await createServerFiles(collectionsState); 248 | await createRouteFile(routes, collectionsState); 249 | } 250 | 251 | 252 | -------------------------------------------------------------------------------- /client/components/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { createMuiTheme, makeStyles, createStyles, Theme } from '@material-ui/core/styles'; 3 | import red from '@material-ui/core/colors/red' 4 | const primary = red['A200'] 5 | import amber from '@material-ui/core/colors/amber' 6 | const secondary = amber['A400'] 7 | import {CssBaseline, ThemeProvider} from "@material-ui/core" 8 | import Grid from '@material-ui/core/Grid' 9 | import Header from './Header'; 10 | import Footer from './Footer' 11 | import Drawer from './MyDrawer' 12 | import { MemoryRouter, Route} from 'react-router'; 13 | 14 | import Home from './Home/Home'; 15 | import MongoDB from './MongoDB/MongoDB'; 16 | import Oak from './Oak/Oak' 17 | import Docker from './Docker/Docker' 18 | import Deno from './Deno/Deno' 19 | 20 | const theme = createMuiTheme({ 21 | palette: { 22 | primary: { 23 | main: primary 24 | }, 25 | secondary: { 26 | main: secondary 27 | }, 28 | type: 'dark' 29 | }, 30 | }); 31 | 32 | const useStyles = makeStyles((theme:Theme) => 33 | createStyles({ 34 | header: { 35 | height: "4.8vh", 36 | }, 37 | content: { 38 | height: "89.8vh", 39 | }, 40 | footer: { 41 | height: "5vh", 42 | }, 43 | }), 44 | ); 45 | 46 | interface Schema { 47 | schemaName: string; 48 | schemaId?: number; 49 | schemaProperties: string[]; 50 | } 51 | 52 | const dockerFileInit: string = ` 53 | FROM hayd/alpine-deno:1.10.2 54 | 55 | # The port that your application listens to. 56 | EXPOSE 3000 57 | 58 | WORKDIR /phlappjack 59 | 60 | # Prefer not to run as root. 61 | # USER deno 62 | 63 | # Cache the dependencies as a layer (the following two steps are re-run only when deps.ts is modified). 64 | # Ideally cache deps.ts will download and compile _all_ external files used in main.ts. 65 | COPY . . 66 | RUN deno cache --unstable deps.ts 67 | RUN deno cache ./client/deps.ts 68 | # RUN deno bundle ./client/index.tsx ./build/bundle.js 69 | 70 | 71 | # These steps will be re-run upon each file change in your working directory: 72 | ADD . . 73 | # Compile the main app so that it doesn't need to be compiled each startup/entry. 74 | # RUN deno cache mod.ts 75 | 76 | CMD ["deno", "run", "-A", "--unstable", "mod.ts"] 77 | ` 78 | 79 | const dockerComposeFileInit: string = ` 80 | version: '3.1' 81 | 82 | services: 83 | mongo: 84 | image: mongo 85 | restart: always 86 | ports: 87 | - 27018:27017 88 | environment: 89 | MONGO_INITDB_ROOT_USERNAME: 90 | MONGO_INITDB_ROOT_PASSWORD: 91 | 92 | phlappjack 93 | image: phlappjack 94 | ports: 95 | - 8000:8000 96 | ` 97 | 98 | export default function App() { 99 | const classes = useStyles() 100 | 101 | //State regarding new application. 102 | const [newApplication, setNewApplication] = useState(''); 103 | //State regarding loading applications. 104 | const [applicationsToLoad, setApplicationsToLoad] = useState(Object.keys(window.localStorage)); 105 | //Toggle state allowing load application text display to turn into text fields. 106 | const loadToggleState = new Array(applicationsToLoad.length).fill(true).map((item, idx) => true); 107 | const [loadToggles, setLoadToggles] = useState(loadToggleState); 108 | const [editLoadTextFieldValue, setEditLoadTextFieldValue] = useState(); 109 | 110 | //State regarding MongoDB tab. 111 | const [dbBeingModified, setDBBeingModified] = useState('Schema Input Field'); 112 | //State for schema models and properties. 113 | const [dbInputDisplay, setDBInputDisplay] = useState({}); 114 | //Toggle state allowing model input text display to turn into text fields. 115 | const [dbToggles, setDBToggles] = useState([]); 116 | 117 | const [editDBTextFieldValue, setEditDBTextFieldValue] = useState(); 118 | 119 | //State for mongo atlas username. 120 | const [atlasUserName, setAtlasUserName] = useState(''); 121 | //State for mongo atlas password. 122 | const [atlasPassword, setAtlasPassword] = useState(''); 123 | //State for mongo atlas cluster. 124 | const [atlasHostCluster, setAtlasHostCluster] = useState(''); 125 | //State for mongo atlas username. 126 | const [atlasDB, setAtlasDB] = useState(''); 127 | 128 | //State regarding Oak Tab. 129 | const [endPoints, setEndPoints] = useState({'/':[]}); 130 | const [selectedEndPoint, setSelectedEndPoint] = useState(''); 131 | //State for routes. 132 | const [routes, setRoutes] = useState([]); 133 | //Toggle state allowing route display to turn into text fields. 134 | const routeToggleState = new Array(routes.length).fill(true).map((item, idx) => true); 135 | const [routeToggles, setRouteToggles] = useState(routeToggleState); 136 | const [editRouteTextFieldValue, setEditRouteTextFieldValue] = useState(); 137 | //State for middleware template. 138 | const [middleWareTemp, setMiddleWareTemp] = useState(``); 139 | 140 | //State regarding Docker Tab. 141 | const [dockerFile, setDockerFile] = useState(dockerFileInit); 142 | const [dockerCompose, setDockerCompose] = useState(dockerComposeFileInit); 143 | 144 | //Key state to allow for re-rendering from child props. 145 | const [childKey, setChildKey] = useState(0); 146 | //Key state to allow for application to re-render upon 147 | //selection of an application to load. 148 | const [childKeyForLoadingApplication, setChildKeyForLoadingApplication] = useState(0); 149 | const [open, setOpen] = useState(false); 150 | 151 | const handleDrawerOpen = () => { 152 | setOpen(true); 153 | }; 154 | 155 | const handleDrawerClose = () => { 156 | setOpen(false); 157 | }; 158 | 159 | useEffect(() => { 160 | 161 | if(applicationsToLoad.indexOf(newApplication) > -1){ 162 | //Retrieve saved props from local storage. 163 | const loadedProps = window.localStorage.getItem(newApplication); 164 | //Deconstruct props to respective variable. 165 | const { 166 | _dbInputDisplay, 167 | _atlasUserName, 168 | _atlasPassword, 169 | _atlasHostCluster, 170 | _atlasDB, 171 | _endPoints, 172 | _routes, 173 | _dockerFile, 174 | _dockerCompose 175 | } = JSON.parse(loadedProps); 176 | //Set the loaded props as the current state. 177 | setDBInputDisplay(_dbInputDisplay); 178 | setAtlasUserName(_atlasUserName); 179 | setAtlasPassword(_atlasPassword); 180 | setAtlasHostCluster(_atlasHostCluster); 181 | setAtlasDB(_atlasDB); 182 | setEndPoints(_endPoints); 183 | setRoutes(_routes); 184 | setDockerFile(_dockerFile); 185 | setDockerCompose(_dockerCompose); 186 | const dbToggleState = new Array(Object.keys(_dbInputDisplay).length).fill(true).map((item, idx) => true); 187 | setDBToggles(dbToggleState) 188 | } 189 | 190 | const newDockerFile: string = `FROM hayd/alpine-deno:1.10.2 191 | 192 | # The port that your application listens to. 193 | EXPOSE 3000 194 | 195 | WORKDIR /${newApplication} 196 | 197 | # Prefer not to run as root. 198 | # USER deno 199 | 200 | # Cache the dependencies as a layer (the following two steps are re-run only when deps.ts is modified). 201 | # Ideally cache deps.ts will download and compile _all_ external files used in main.ts. 202 | COPY . . 203 | RUN deno cache --unstable deps.ts 204 | RUN deno cache ./client/deps.ts 205 | # RUN deno bundle ./client /index.tsx ./build/bundle.js 206 | 207 | 208 | # These steps will be re-run upon each file change in your working directory: 209 | ADD . . 210 | # Compile the main app so that it doesn't need to be compiled each startup/entry. 211 | # RUN deno cache mod.ts 212 | 213 | CMD ["deno", "run", "-A", "--unstable", "mod.ts"] 214 | ` 215 | setDockerFile(newDockerFile); 216 | 217 | 218 | const newDockerComposeFile: string = `version: '3.1' 219 | 220 | services: 221 | mongo: 222 | image: mongo 223 | restart: always 224 | ports: 225 | - 27018:27017 226 | environment: 227 | MONGO_INITDB_ROOT_USERNAME: ${atlasUserName} 228 | MONGO_INITDB_ROOT_PASSWORD: ${atlasPassword} 229 | 230 | ${newApplication}: 231 | image:${newApplication} 232 | ports: 233 | - 8000:8000 234 | ` 235 | setDockerCompose(newDockerComposeFile); 236 | 237 | },[newApplication]) 238 | 239 | return ( 240 | 241 | 242 | 243 | 244 | 245 |
    250 | 251 | 252 | 253 | 281 | 282 | 283 | 284 | 298 | 299 | 300 | 303 | 304 | 305 | 329 | 330 | 331 | 349 | 350 | 351 | 371 | 372 | 373 | 374 | 375 |
    376 |
    377 | 378 | 379 | 380 | ) 381 | } 382 | -------------------------------------------------------------------------------- /assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | --------------------------------------------------------------------------------