Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes for local development and PostgREST data views #255

Merged
merged 10 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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