Skip to content

Commit

Permalink
Merge pull request #255 from UW-Macrostrat/local-ssl
Browse files Browse the repository at this point in the history
Changes for local development and PostgREST data views
  • Loading branch information
davenquinn authored Oct 21, 2024
2 parents 9f1b17d + efc706d commit bb18484
Show file tree
Hide file tree
Showing 19 changed files with 417 additions and 67 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ docker-compose.yaml
*.png
*.jpg

# Certificates for localhost SSL
*.pem


.vite
37 changes: 32 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@

Macrostrat's map interface is web portal to a geologic model of the Earth's crust.

Version 5 of the application transitions to using [Vite](https://vitejs.dev/) for bundling and [Vike](https://vike.dev/) for server-side rendering. We are working on updating this version for performance and stability.
Version 5 of the application transitions to using [Vite](https://vitejs.dev/) for bundling and [Vike](https://vike.dev/)
for server-side rendering. We are working on updating this version for performance and stability.

## Installation for local development

1. Clone the repository
2. Pull down submodules (`git submodule update --init --recursive`)
3. Create and populate a `.env` file with the appropriate environment variables (See [`.env.example`](https://github.com/UW-Macrostrat/web/blob/main/.env.example) for more information.)
4. Verify that you have access to recent versions of Node.js and the Yarn package manager ( `node >= 16.0.0` and `yarn >= 4.0.0`; run `node -v` and `yarn -v` to check)
3. Create and populate a `.env` file with the appropriate environment variables (See [
`.env.example`](https://github.com/UW-Macrostrat/web/blob/main/.env.example) for more information.)
4. Verify that you have access to recent versions of Node.js and the Yarn package manager ( `node >= 16.0.0` and
`yarn >= 4.0.0`; run `node -v` and `yarn -v` to check)
5. Run `yarn install` to update packages
6. Start the live-reloading development server with `yarn run dev`. The server will be available at `http://localhost:3000` by default.
6. Start the live-reloading development server with `yarn run dev`. The server will be available at
`http://localhost:3000` by default.

## Contributing

Expand Down Expand Up @@ -53,4 +57,27 @@ To deploy to kubernetes there is two steps.

2. Update the deployment in Kubernetes

You do this by updating the image tag here to whatever you tagged above: https://github.com/UW-Macrostrat/tiger-macrostrat-config/blob/main/manifests/development/web/deployment-patch.yaml
You do this by updating the image tag here to whatever you tagged
above: https://github.com/UW-Macrostrat/tiger-macrostrat-config/blob/main/manifests/development/web/deployment-patch.yaml

## Local development with SSL

You can generate self-signed SSL certificates for local development using the following command:

```bash
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \
-keyout private-key.pem -out certificate.pem
```

To run the development server with SSL, you can use the following command:

## Testing authentication on localhost

If you are developing locally and need to test authentication, you can
use a plugin like **CookieSync** to automatically pull cookies from the production or development
site into your local environment. This will allow you to use the same session
information locally. The cookie that must be copied is called `access_token`.

We will eventually build in a shim authentication service to allow for easier
local development.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
"@typescript-eslint/eslint-plugin": "^6.3.0",
"@typescript-eslint/parser": "^6.3.0",
"@yarnpkg/sdks": "^3.1.0",
"chalk": "^5.3.0",
"http-proxy-middleware": "^3.0.3",
"prettier": "^2.7.1",
"react-arborist": "^3.4.0",
"react-text-annotate-blend": "^1.2.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const apiV2Prefix = getRuntimeConfig(

export const ingestPrefix = getRuntimeConfig(
"MACROSTRAT_INGEST_API",
apiDomain + "/api/ingest"
apiDomain + "/api/v3"
);

export const cdrPrefix = getRuntimeConfig("CDR_API_URL");
Expand Down
1 change: 1 addition & 0 deletions pages/integrations/xdd/+Page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ for discovering data from the scientific literature.

- Stratigraphic units linked to papers in Rockd and Macrostrat
- Extracting [structured data](/integrations/xdd/extractions) from papers
- [Data types](/integrations/xdd/types) for feedback
- [Model runs](/integrations/xdd/runs)
- [Feedback](/integrations/xdd/feedback) on extractions
- Training machine learning models for [Map legend affinity](/dev/legend-affinity)
66 changes: 50 additions & 16 deletions pages/integrations/xdd/feedback/@sourceTextID/+Page.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import {
usePostgresQuery,
} from "../../extractions/lib/data-service";
import { FeedbackComponent } from "./lib";
import { OverlaysProvider } from "@blueprintjs/core";
import { Intent, NonIdealState, OverlaysProvider } from "@blueprintjs/core";
import { ErrorBoundary, Pagination } from "@macrostrat/ui-components";
import { useState } from "react";

/**
* Get a single text window for feedback purposes
Expand Down Expand Up @@ -42,25 +44,57 @@ function ExtractionIndex() {
return h("div", "Loading...");
}

console.log(data);

return h(
"div.feedback-windows",
data.map((d) => {
console.log(data);
const window = enhanceData(d, models, entityTypes);
const { entities = [], paragraph_text, model } = window;
//h("h1", paper.citation?.title ?? "Model extractions"),
return h(FeedbackComponent, {
entities,
text: paragraph_text,
model,
entityTypes,
sourceTextID: window.source_text,
runID: window.model_run,
});
})
ErrorBoundary,
h(MultiFeedbackInterface, { data, models, entityTypes })
);
}

function MultiFeedbackInterface({ data, models, entityTypes }) {
const [ix, setIX] = useState(0);
const currentData = data[ix];
const count = data.length;

return h("div.feedback", [
h.if(data.length > 1)([
h(NonIdealState, {
icon: "warning-sign",
title: "Multiple model runs for feedback",
description: `Showing entities from ${
ix + 1
} of ${count} model runs. Merging several runs is not yet supported.`,
}),
h(Pagination, {
count,
page: ix,
setPage: setIX,
nextDisabled: ix >= count - 1,
}),
]),
h(FeedbackInterface, {
data: currentData,
models,
entityTypes,
}),
]);
}

function FeedbackInterface({ data, models, entityTypes }) {
const window = enhanceData(data, models, entityTypes);
const { entities = [], paragraph_text, model } = window;
return h(FeedbackComponent, {
entities,
text: paragraph_text,
model,
entityTypes,
sourceTextID: window.source_text,
runID: window.model_run,
});
}


// function FeedbackDevTool() {
// const entities = useStore((state) => state.entities);
// if (entities == null)
Expand Down
11 changes: 11 additions & 0 deletions pages/integrations/xdd/feedback/@sourceTextID/+config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default {
meta: {
Page: {
env: {
client: true,
server: false,
},
},
},
clientRouting: false,
};
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,6 @@ function prepareGraphForServer(tree: TreeData[]): GraphData {
};

nodeMap.set(node.id, node);

nodes.push(nodeData);

if (node.children) {
Expand Down
4 changes: 0 additions & 4 deletions pages/integrations/xdd/feedback/@sourceTextID/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ import { ButtonGroup, Card } from "@blueprintjs/core";
import { OmniboxSelector } from "./type-selector";
import { CancelButton, SaveButton } from "@macrostrat/ui-components";

export interface FeedbackComponentProps {
// Add props here
}

function setsAreTheSame<T>(a: Set<T>, b: Set<T>) {
if (a.size !== b.size) return false;
for (const item of a) {
Expand Down
55 changes: 55 additions & 0 deletions pages/integrations/xdd/types/+Page.client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { FullscreenPage } from "~/layouts";
import h from "@macrostrat/hyper";
import { PageBreadcrumbs } from "~/components";
import { PostgRESTTableView } from "~/components/legend-table";

import {
ColorCell,
EditableTextArea,
ColorPicker,
} from "@macrostrat/data-sheet2";
import { asChromaColor } from "@macrostrat/color-utils";
import { LoginButton } from "#/maps/ingestion/components/navbar";
import { AuthStatus } from "@macrostrat/auth-components";

const colorField = {
name: "Color",
key: "color",
required: false,
transform: (d) => d,
dataEditor: ColorPicker,
valueRenderer: (d) => {
let color = asChromaColor(d);
return color?.name() ?? "";
},
// Maybe this should be changed to CellProps?
cellComponent: ColorCell,
};

export function Page() {
return h(FullscreenPage, { className: "main" }, [
h(PageBreadcrumbs),
h("div.header", [h("h1", "Entity types"), h("div.spacer"), h(AuthStatus)]),
h(PostgRESTTableView, {
table: "kg_entity_type",
editable: true,
columnOptions: {
omitColumns: ["id"],
overrides: {
color: colorField,
name: {
name: "Name",
style: { fontFamily: "monospace" },
},
description: {
name: "Description",
editable: true,
//inlineEditor: true,
dataEditor: EditableTextArea,
},
},
},
order: { key: "id", ascending: true },
}),
]);
}
30 changes: 30 additions & 0 deletions server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { createMiddleware } from "@universal-middleware/express";
import { createMacrostratQlrAPI } from "@macrostrat-web/qgis-integration";
import express from "express";
import sirv from "sirv";
import chalk from "chalk";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
Expand Down Expand Up @@ -65,6 +66,7 @@ async function startServer() {
const app = express();

app.use(compression());

//
if (isProduction) {
app.use(sirv(`${root}/dist/client`));
Expand All @@ -73,6 +75,34 @@ async function startServer() {
// Ideally we'd be able to remove this fix.
app.use("/cesium", sirv(`${root}/dist/cesium`));
} else {
// For localhost development: create a proxy to the API server to enable
// API requests with the appropriate authorization cookies or headers.
const proxyDomain = process.env.MACROSTRAT_API_PROXY_DOMAIN;
if (proxyDomain) {
const target = proxyDomain + "/api";
console.log("Proxying API requests to", target);
const { createProxyMiddleware } = await import("http-proxy-middleware");
app.use(
"/api",
createProxyMiddleware({
target,
changeOrigin: true,
on: {
proxyReq: (proxyReq) => {
const parsedPath = new URL(proxyReq.path, proxyDomain);
console.log(
chalk.bold.green(`[${proxyReq.method}]`),
chalk.dim(proxyDomain) +
parsedPath.pathname +
chalk.dim(parsedPath.hash) +
chalk.dim(parsedPath.search)
);
},
},
})
);
}

// Instantiate Vite's development server and integrate its middleware to our server.
// ⚠️ We should instantiate it *only* in development. (It isn't needed in production
// and would unnecessarily bloat our server in production.)
Expand Down
8 changes: 2 additions & 6 deletions server/vike-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,21 @@ export async function vikeHandler<
}

async function getUserFromCookie(cookies: Record<string, string>) {
const isProduction = process.env.NODE_ENV === "production";
// Pull out the authorization cookie and decrypt it
let user: any = undefined;
try {
const authHeader = cookies?.Authorization;
const authHeader = cookies?.["access_token"];
const secret = new TextEncoder().encode(process.env.SECRET_KEY);
const jwt = authHeader.substring(7, authHeader.length);
// We probably don't need to verify the JWT on each request.
// OR we can pass the user obju
user = (await jose.jwtVerify(jwt, secret)).payload;
console.log("User", user);
} catch (e) {
// I don't care if it fails, it just means the user isn't logged in
console.log("Anonymous user");
}

if (!isProduction && process.env.DEV_ENABLE_AUTH !== "true") {
// Haha wow this is sketchy...this needs to be stopped.
user = { groups: [1] };
}
return user;
}

Expand Down
1 change: 1 addition & 0 deletions src/_providers/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ async function authTransformer(
case "login":
// Assemble the return URL on click based on the current page
const return_url = window.location.origin + window.location.pathname;
console.log("Returning to", return_url);
window.location.href = `${ingestPrefix}/security/login?return_url=${return_url}`;
case "logout":
// Delete the token from the session
Expand Down
Loading

0 comments on commit bb18484

Please sign in to comment.