├── 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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 | [](https://github.com/mandarineorg/leaf)
4 |
5 | 
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 | 
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 |
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 |
132 |
133 | : }
141 | onClick={handleButtonClickGit}
142 | >
143 | {gitSuccess ? "Cloned" : "Clone"}
144 |
145 | {gitLoading && }
146 |
147 |
148 |
:
}
156 | onClick={handleButtonClickDeploy}
157 | >
158 | {deploySuccess ? "Deployed" : "Deploy"}
159 |
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 | }
226 | onClick = {saveApplication}
227 | >
228 | Save
229 |
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 |
251 |
252 |
253 |
281 |
282 |
283 |
284 |
298 |
299 |
300 |
303 |
304 |
305 |
329 |
330 |
331 |
349 |
350 |
351 |
371 |
372 |
373 |
374 |
377 |
378 |
379 |
380 | )
381 | }
382 |
--------------------------------------------------------------------------------
/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
306 |
--------------------------------------------------------------------------------