├── api ├── .gitignore ├── Dockerfile ├── src │ ├── errors.rs │ ├── config.rs │ ├── main.rs │ ├── lib.rs │ └── routes.rs ├── Cargo.toml └── Cargo.lock ├── pgDagger ├── prometheus │ └── config │ │ ├── Makefile │ │ └── prometheus.yml ├── grafana │ ├── dashboards.yaml │ └── datasources │ │ └── prometheus.yaml ├── scripts │ ├── Dockerfile-pgrst.init │ ├── Dockerfile-arbitrary-pg.init │ ├── arbitrary_pg_init.py │ └── pgrst_init.py ├── README.md ├── docker-compose.yml ├── LICENSE └── postgres.conf ├── README.md └── pgUI ├── public ├── favicon.ico ├── coredb-logo.png ├── coredb-logo-globe.png ├── icons │ ├── activity.svg │ ├── terminal.svg │ ├── compass.svg │ ├── database.svg │ ├── server.svg │ ├── sliders.svg │ ├── users.svg │ ├── iconList.tsx │ └── codesandbox.svg └── vercel.svg ├── styles ├── globals.css └── globals.scss ├── postcss.config.js ├── svgrrc.js ├── .prettierrc.json ├── README.md ├── components ├── Main │ ├── Main.module.scss │ └── index.tsx ├── Card │ ├── index.tsx │ └── Card.module.scss ├── Chip │ ├── Chip.module.scss │ └── index.tsx ├── InstanceNav │ ├── InstanceNav.module.scss │ ├── instanceNavData.ts │ └── index.tsx ├── Button │ ├── index.tsx │ └── Button.module.scss ├── Logo │ └── index.tsx ├── SearchBar │ ├── index.tsx │ └── SearchBar.module.scss ├── Header │ ├── Header.module.scss │ └── index.tsx ├── IconButton │ └── index.tsx ├── InstanceCard │ ├── InstanceCard.module.scss │ └── index.tsx └── Tooltip │ ├── index.tsx │ └── Tooltip.module.scss ├── tailwind.config.js ├── next.config.js ├── .storybook ├── preview.ts └── main.ts ├── .babelrc.json ├── pages ├── api │ └── hello.ts ├── instances │ └── [id].tsx ├── _app.tsx └── index.tsx ├── .gitignore ├── tsconfig.json ├── stories ├── InstanceNav.stories.ts ├── IconButton.stories.ts ├── Button.stories.ts ├── assets │ ├── direction.svg │ ├── flow.svg │ ├── code-brackets.svg │ ├── comments.svg │ ├── repo.svg │ ├── plugin.svg │ ├── stackalt.svg │ └── colors.svg └── Introduction.mdx ├── package.json └── LICENSE /api/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /pgDagger/prometheus/config/Makefile: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pgUI 2 | 3 | An experimental user interface for PostgreSQL. 4 | -------------------------------------------------------------------------------- /pgUI/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tembo-io/pgUI/HEAD/pgUI/public/favicon.ico -------------------------------------------------------------------------------- /pgUI/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /pgUI/public/coredb-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tembo-io/pgUI/HEAD/pgUI/public/coredb-logo.png -------------------------------------------------------------------------------- /pgUI/public/coredb-logo-globe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tembo-io/pgUI/HEAD/pgUI/public/coredb-logo-globe.png -------------------------------------------------------------------------------- /pgUI/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /pgUI/svgrrc.js: -------------------------------------------------------------------------------- 1 | // .svgrrc.js 2 | module.exports = { 3 | // icon: true, 4 | // expandProps: false, 5 | typescript: true, 6 | }; 7 | -------------------------------------------------------------------------------- /pgUI/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true, 6 | "bracketSameLine": true 7 | } 8 | -------------------------------------------------------------------------------- /pgDagger/grafana/dashboards.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: Default 5 | folder: Postgres 6 | type: file 7 | options: 8 | path: /etc/grafana/extra-dashboards 9 | -------------------------------------------------------------------------------- /pgDagger/scripts/Dockerfile-pgrst.init: -------------------------------------------------------------------------------- 1 | FROM python:3.11.2-slim-bullseye 2 | 3 | RUN pip3 install psycopg2-binary sqlalchemy tenacity 4 | 5 | COPY pgrst_init.py . 6 | 7 | CMD python3 pgrst_init.py 8 | -------------------------------------------------------------------------------- /pgDagger/scripts/Dockerfile-arbitrary-pg.init: -------------------------------------------------------------------------------- 1 | FROM python:3.11.2-slim-bullseye 2 | 3 | RUN pip3 install psycopg2-binary sqlalchemy tenacity 4 | 5 | COPY arbitrary_pg_init.py . 6 | 7 | CMD python3 arbitrary_pg_init.py 8 | -------------------------------------------------------------------------------- /pgUI/public/icons/activity.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pgDagger/grafana/datasources/prometheus.yaml: -------------------------------------------------------------------------------- 1 | datasources: 2 | - name: Prometheus 3 | type: prometheus 4 | isDefault: false 5 | url: http://prometheus:9090 6 | - name: Promscale-PromQL 7 | type: prometheus 8 | isDefault: true 9 | url: http://promscale:9201 10 | -------------------------------------------------------------------------------- /pgUI/README.md: -------------------------------------------------------------------------------- 1 | # pgUI 2 | 3 | pgUI is an open-source, extensible web UI for Postgres and Postgres extensions. 4 | 5 | Start Nextjs 6 | 7 | ``` 8 | npm install 9 | npm run dev 10 | ``` 11 | 12 | ### Component Library via Storybook 13 | 14 | ``` 15 | npm run storybook 16 | ``` 17 | -------------------------------------------------------------------------------- /pgUI/components/Main/Main.module.scss: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | display: grid; 3 | width: 100%; 4 | height: 100vh; 5 | background-color: var(--primary-3); 6 | &.leftBar { 7 | grid-template-columns: 3.5rem 1fr; 8 | } 9 | 10 | .content { 11 | padding: var(--space-4) var(--space-8); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pgUI/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | './pages/**/*.{js,ts,jsx,tsx}', 5 | './components/**/*.{js,ts,jsx,tsx}', 6 | './app/**/*.{js,ts,jsx,tsx}', 7 | ], 8 | theme: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /pgUI/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | module.exports = { 3 | reactStrictMode: true, 4 | webpack(config) { 5 | config.module.rules.push({ 6 | test: /\.svg$/i, 7 | issuer: /\.[jt]sx?$/, 8 | use: ['@svgr/webpack'], 9 | }); 10 | 11 | return config; 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /pgUI/.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import '../styles/globals.scss'; 2 | 3 | export const parameters = { 4 | backgrounds: { 5 | default: 'light', 6 | }, 7 | actions: { argTypesRegex: '^on[A-Z].*' }, 8 | controls: { 9 | matchers: { 10 | color: /(background|color)$/i, 11 | date: /Date$/, 12 | }, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /pgUI/components/Card/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | import styles from './Card.module.scss'; 4 | 5 | interface CardProps { 6 | children: React.ReactNode; 7 | } 8 | 9 | const Card: FC = ({ children }) => { 10 | return
{children}
; 11 | }; 12 | 13 | export default Card; 14 | -------------------------------------------------------------------------------- /pgUI/public/icons/terminal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/coredb/rust:1.66.1 as builder 2 | COPY Cargo.toml Cargo.lock ./ 3 | COPY src ./src 4 | RUN cargo build --release && \ 5 | cargo clean -p pgui-api 6 | RUN cargo install --path . 7 | 8 | FROM quay.io/coredb/rust:1.66.1-slim-buster 9 | COPY --from=builder /usr/local/cargo/bin/* /usr/local/bin/ 10 | ENV RUST_LOG=info 11 | -------------------------------------------------------------------------------- /pgUI/.babelrc.json: -------------------------------------------------------------------------------- 1 | // storybook req 2 | { 3 | "sourceType": "unambiguous", 4 | "presets": [ 5 | [ 6 | "@babel/preset-env", 7 | { 8 | "targets": { 9 | "chrome": 100 10 | } 11 | } 12 | ], 13 | "@babel/preset-typescript", 14 | "@babel/preset-react" 15 | ], 16 | "plugins": [] 17 | } 18 | -------------------------------------------------------------------------------- /pgUI/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next'; 3 | 4 | type Data = { 5 | name: string; 6 | }; 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }); 13 | } 14 | -------------------------------------------------------------------------------- /pgUI/components/Card/Card.module.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | position: relative; 3 | padding: var(--space-4); 4 | background-color: var(--primary-2); 5 | border: 1px solid var(--primary-4); 6 | border-radius: var(--border-radius); 7 | min-width: 10rem; 8 | transition: all 280ms ease; 9 | 10 | &:hover { 11 | cursor: pointer; 12 | border: 1px solid var(--primary-5); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /api/src/errors.rs: -------------------------------------------------------------------------------- 1 | //! Custom errors types for pgui-api 2 | use thiserror::Error; 3 | use url::ParseError; 4 | 5 | #[derive(Error, Debug)] 6 | pub enum PgUIAPIError { 7 | /// a url parsing error 8 | #[error("url parsing error {0}")] 9 | UrlParsingError(#[from] ParseError), 10 | 11 | /// a database error 12 | #[error("database error {0}")] 13 | DatabaseError(#[from] sqlx::Error), 14 | } 15 | -------------------------------------------------------------------------------- /pgUI/components/Chip/Chip.module.scss: -------------------------------------------------------------------------------- 1 | .chip { 2 | display: flex; 3 | align-items: center; 4 | 5 | .colorSwatch { 6 | width: var(--space-2); 7 | height: var(--space-2); 8 | border-radius: 50%; 9 | margin-right: var(--space-1); 10 | } 11 | 12 | span { 13 | font-size: var(--size-xs); 14 | text-transform: uppercase; 15 | letter-spacing: 1px; 16 | color: var(--primary-7); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pgUI/components/InstanceNav/InstanceNav.module.scss: -------------------------------------------------------------------------------- 1 | .instanceNav { 2 | height: calc(100vh - 3.5rem); 3 | width: 3.5rem; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: flex-start; 8 | background-color: var(--primary-3); 9 | padding-top: var(--space-3); 10 | border-right: 1px solid var(--primary-4); 11 | 12 | .link { 13 | margin: var(--space-2) 0; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pgUI/components/Button/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | import styles from './Button.module.scss'; 4 | 5 | interface ButtonProps { 6 | children: React.ReactNode; 7 | onClick?(): any; 8 | } 9 | 10 | const Button: FC = ({ children, onClick }) => { 11 | return ( 12 | 15 | ); 16 | }; 17 | 18 | export default Button; 19 | -------------------------------------------------------------------------------- /pgUI/components/Logo/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Image from 'next/image'; 3 | 4 | import RawLogo from '/public/coredb-logo-globe.png'; // https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping 5 | 6 | export default function Logo() { 7 | return ( 8 | The CoreDB logo - a multicolored globe 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /pgUI/components/SearchBar/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import styles from './SearchBar.module.scss'; 3 | 4 | interface SearchProps { 5 | placeholder?: string; 6 | } 7 | 8 | const SearchBar: FC = ({ placeholder = 'Search Instances' }) => { 9 | return ( 10 |
11 | 12 |
13 | ); 14 | }; 15 | 16 | export default SearchBar; 17 | -------------------------------------------------------------------------------- /pgUI/public/icons/compass.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /pgUI/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@storybook/react-webpack5'; 2 | const config: StorybookConfig = { 3 | stories: ['../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'], 4 | addons: [ 5 | '@storybook/addon-links', 6 | '@storybook/addon-essentials', 7 | '@storybook/addon-interactions', 8 | '@storybook/preset-scss', 9 | ], 10 | framework: { 11 | name: '@storybook/react-webpack5', 12 | options: {}, 13 | }, 14 | docs: { 15 | autodocs: 'tag', 16 | }, 17 | }; 18 | export default config; 19 | -------------------------------------------------------------------------------- /pgUI/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | # storybook 39 | storybook-static 40 | -------------------------------------------------------------------------------- /pgUI/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /pgUI/public/icons/database.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /pgUI/components/Main/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, ReactNode } from 'react'; 2 | import cx from 'classnames'; 3 | import styles from './Main.module.scss'; 4 | import InstanceNav from '../InstanceNav'; 5 | 6 | interface Props { 7 | children: ReactNode; 8 | hasLeftBar: boolean; 9 | } 10 | 11 | const Main: FC = ({ children, hasLeftBar = false }) => { 12 | return ( 13 |
14 | {hasLeftBar && } 15 |
{children}
16 |
17 | ); 18 | }; 19 | 20 | export default Main; 21 | -------------------------------------------------------------------------------- /api/src/config.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | #[derive(Debug)] 4 | pub struct Config { 5 | pub pg_conn_str: String, 6 | } 7 | 8 | impl Default for Config { 9 | fn default() -> Self { 10 | Self { 11 | pg_conn_str: from_env_default( 12 | "POSTGRES_CONNECTION", 13 | "postgresql://postgres:postgres@0.0.0.0:5432/postgres", 14 | ), 15 | } 16 | } 17 | } 18 | 19 | /// source a variable from environment - use default if not exists 20 | fn from_env_default(key: &str, default: &str) -> String { 21 | env::var(key).unwrap_or_else(|_| default.to_owned()) 22 | } 23 | -------------------------------------------------------------------------------- /pgUI/stories/InstanceNav.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | 3 | import InstanceNav from '../components/InstanceNav'; 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction 6 | const meta = { 7 | title: 'Example/InstanceNav', 8 | component: InstanceNav, 9 | tags: ['autodocs'], 10 | argTypes: {}, 11 | } satisfies Meta; 12 | 13 | export default meta; 14 | type Story = StoryObj; 15 | 16 | // More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args 17 | export const Basic: Story = { 18 | args: {}, 19 | }; 20 | -------------------------------------------------------------------------------- /api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pgui-api" 3 | version = "0.1.1" 4 | edition = "2021" 5 | authors = ["CoreDB.io"] 6 | description = "A backend API for pgUI." 7 | homepage = "https://www.coredb.io" 8 | keywords = ["api", "postgres"] 9 | license = "Apache-2.0" 10 | repository = "https://github.com/CoreDB-io/coredb/tree/main/pgUI/api" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | actix-web = "4.3.0" 16 | regex = "1.7.1" 17 | url = "2.3.1" 18 | sqlx = { version = "0.6", features = [ "runtime-tokio-native-tls" , "postgres", "chrono" ] } 19 | log = "0.4.17" 20 | thiserror = "1.0.38" 21 | base64 = "0.21.0" 22 | -------------------------------------------------------------------------------- /pgUI/components/Chip/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | import styles from './Chip.module.scss'; 4 | 5 | interface Props { 6 | label: string; 7 | // This could be improved with specific types but for now 8 | // they represent color vars 9 | type: 10 | | 'accent' 11 | | 'success' 12 | | 'error' 13 | | 'primary-5' 14 | | 'primary-9' 15 | | 'accent-darker'; 16 | } 17 | 18 | const Chip: FC = ({ label, type }) => { 19 | return ( 20 |
21 |
25 | {label} 26 |
27 | ); 28 | }; 29 | 30 | export default Chip; 31 | -------------------------------------------------------------------------------- /pgUI/pages/instances/[id].tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useRouter } from 'next/router'; 3 | import Head from 'next/head'; 4 | 5 | import Header from '../../components/Header'; 6 | import Main from '../../components/Main'; 7 | 8 | const Instance = () => { 9 | const router = useRouter(); 10 | const { id } = router.query; 11 | 12 | return ( 13 | <> 14 | 15 | CoreDB 16 | 17 | 18 | 19 |
20 |
21 |

Instance: {id}

22 |
23 | 24 | ); 25 | }; 26 | 27 | export default Instance; 28 | -------------------------------------------------------------------------------- /pgUI/public/icons/server.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /pgUI/stories/IconButton.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | 3 | import IconButton from '../components/IconButton'; 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction 6 | const meta = { 7 | title: 'Example/IconButton', 8 | component: IconButton, 9 | tags: ['autodocs'], 10 | argTypes: {}, 11 | } satisfies Meta; 12 | 13 | export default meta; 14 | type Story = StoryObj; 15 | 16 | // More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args 17 | export const Basic: Story = { 18 | args: { 19 | iconName: 'codesandbox', 20 | onClick: () => alert('you clicked the button') 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /pgUI/components/SearchBar/SearchBar.module.scss: -------------------------------------------------------------------------------- 1 | .SearchBar { 2 | flex: 1; 3 | min-width: 50%; 4 | width: auto; 5 | margin-bottom: var(--space-5); 6 | 7 | input { 8 | width: 100%; 9 | height: 2.75rem; 10 | padding: var(--space-2); 11 | font-size: var(--size-m); 12 | border: 1px solid var(--primary-4); 13 | border-radius: var(--unit); 14 | 15 | &:focus { 16 | outline: solid 1px var(--accent); 17 | } 18 | } 19 | } 20 | 21 | .filters { 22 | height: 2.75rem; 23 | margin: 0 var(--space-5); 24 | padding: var(--space-2); 25 | background-color: var(--primary-1); 26 | border: 1px solid var(--primary-4); 27 | border-radius: var(--unit); 28 | 29 | > svg { 30 | margin: 0 var(--unit); 31 | } 32 | } -------------------------------------------------------------------------------- /pgUI/stories/Button.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | // import iconList from '../public/icons/iconList'; 3 | 4 | // import { Button } from './Button'; 5 | import Button from '../components/Button'; 6 | 7 | // More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction 8 | const meta = { 9 | title: 'Example/Button', 10 | component: Button, 11 | tags: ['autodocs'], 12 | argTypes: {}, 13 | } satisfies Meta; 14 | 15 | export default meta; 16 | type Story = StoryObj; 17 | 18 | // More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args 19 | export const Basic: Story = { 20 | args: { 21 | children: 'Add Instance', 22 | onClick: () => alert('you clicked the button') 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /pgUI/components/Header/Header.module.scss: -------------------------------------------------------------------------------- 1 | .Header { 2 | width: 100%; 3 | height: 3.5rem; 4 | font-size: var(--size-s); 5 | padding: var(--space-3); 6 | background-color: var(--primary-8); 7 | color: var(--primary-3); 8 | 9 | h4 { 10 | font-weight: var(--normal-w); 11 | } 12 | 13 | nav { 14 | margin-right: var(--space-5); 15 | li { 16 | margin-left: var(--space-3); 17 | list-style: none; 18 | &:hover { 19 | color: var(--primary-2); 20 | } 21 | } 22 | } 23 | 24 | .logoGroup { 25 | margin-right: var(--space-5); 26 | h1 { 27 | // hide the coredb name 28 | display: none; 29 | visibility: hidden; 30 | font-size: var(--size-l); 31 | } 32 | } 33 | 34 | .UserTag { 35 | padding-left: var(--space-5); 36 | border-left: 1px solid var(--primary-6); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /api/src/main.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{web, App, HttpServer}; 2 | use pgui_api::routes::get_queries; 3 | use pgui_api::{config, connect, routes}; 4 | 5 | // pgUI will make requests to this webserver in order to retrieve data it needs to present (SQL query 6 | // data, time series data, etc) 7 | 8 | #[actix_web::main] 9 | async fn main() -> std::io::Result<()> { 10 | // load configurations from environment 11 | let cfg = config::Config::default(); 12 | // Initialize connection to backend postgresql server 13 | let conn = connect(&cfg.pg_conn_str).await.unwrap(); 14 | 15 | HttpServer::new(move || { 16 | App::new() 17 | .app_data(web::Data::new(conn.clone())) 18 | .service(routes::running) 19 | .service(routes::connection) 20 | .service(get_queries) 21 | }) 22 | .bind(("0.0.0.0", 8080))? 23 | .run() 24 | .await 25 | } 26 | -------------------------------------------------------------------------------- /pgDagger/scripts/arbitrary_pg_init.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from sqlalchemy import create_engine, text 5 | from tenacity import retry, stop_after_attempt, wait_fixed 6 | 7 | POSTGRES_CONNECTION = os.getenv("POSTGRES_CONNECTION", "postgresql://postgres:postgres@0.0.0.0:5433/postgres") 8 | 9 | logging.basicConfig(level=logging.DEBUG) 10 | 11 | # retry 5 times, wait 2 seconds between each retry 12 | @retry(stop=stop_after_attempt(5), wait=wait_fixed(2)) 13 | def create_extensions(): 14 | engine = create_engine(POSTGRES_CONNECTION) 15 | query = "CREATE EXTENSION pg_stat_statements" 16 | with engine.connect() as con: 17 | con.execute(text(query)) 18 | con.commit() 19 | logging.info("created extension pg_stat_statements") 20 | 21 | if __name__ == "__main__": 22 | logging.info("Creating extensions for arbitrary postgres") 23 | create_extensions() 24 | -------------------------------------------------------------------------------- /pgUI/components/InstanceNav/instanceNavData.ts: -------------------------------------------------------------------------------- 1 | interface Option { 2 | label: string; 3 | link: string; 4 | iconName: 5 | | 'activity' 6 | | 'codesandbox' 7 | | 'compass' 8 | | 'database' 9 | | 'server' 10 | | 'sliders' 11 | | 'terminal' 12 | | 'users'; 13 | } 14 | 15 | const navOptions: Array