Skip to content

Commit b65207c

Browse files
authored
Merge pull request #102 from UW-Macrostrat/edit-ssr
Edit ssr
2 parents 1a1b4b3 + cde4aa9 commit b65207c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2289
-25
lines changed

.env.example

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@ VITE_MACROSTRAT_TILESERVER_V2='https://dev.macrostrat.org/tiles'
33
VITE_MACROSTRAT_TILESERVER_V1='https://tiles.macrostrat.org'
44
VITE_MACROSTRAT_TILESERVER_DOMAIN='https://dev.macrostrat.org/tiles'
55
VITE_MACROSTRAT_API_DOMAIN='https://macrostrat.org'
6+
VITE_MACROSTRAT_INGEST_API=https://dev.macrostrat.org/api/ingest
67
VITE_CORELLE_API_DOMAIN='https://rotate.macrostrat.org'
7-
PUBLIC_URL='/'
8+
PUBLIC_URL='/'
9+
SECRET_KEY='Replace with api signing key'

.github/workflows/build-dev.yaml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ name: Build Development
22

33
on:
44
push:
5+
branches: [ '**' ]
56
tags:
67
- v[0-9]+.[0-9]+.[0-9]+-** # Semver Pre-Release
78
pull_request:
@@ -25,7 +26,10 @@ jobs:
2526
tags: |
2627
type=semver,pattern={{version}}
2728
type=raw,value=latest-itb
28-
type=ref,enable=true,prefix=pr-,suffix=-{{date 'YYYYMMDDHHmmss'}},event=pr
29+
type=ref,event=pr,suffix=-{{date 'YYYYMMDDHHmmss'}}
30+
type=ref,event=branch,suffix=-{{date 'YYYYMMDDHHmmss'}}
31+
type=ref,event=tag,suffix=-{{date 'YYYYMMDDHHmmss'}}
32+
type=raw,value=latest-itb-{{date 'YYYYMMDDHHmmss'}}
2933
type=raw,value=sha-{{sha}}
3034
-
3135
name: Set up Docker Buildx
@@ -39,7 +43,7 @@ jobs:
3943
password: ${{ secrets.HARBOR_CLI_SECRET }}
4044
-
4145
name: Build and push
42-
uses: docker/build-push-action@v3
46+
uses: docker/build-push-action@v5
4347
with:
4448
context: .
4549
push: true

.github/workflows/build-prod.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ jobs:
2121
with:
2222
images: hub.opensciencegrid.org/macrostrat/web
2323
tags: |
24+
type=ref,event=pr,suffix=-{{date 'YYYYMMDDHHmmss'}}
25+
type=ref,event=branch,suffix=-{{date 'YYYYMMDDHHmmss'}}
2426
type=semver,pattern={{version}}
2527
type=raw,value=latest,enable={{is_default_branch}}
2628
type=raw,value=sha-{{sha}}
@@ -36,7 +38,7 @@ jobs:
3638
password: ${{ secrets.HARBOR_CLI_SECRET }}
3739
-
3840
name: Build and push
39-
uses: docker/build-push-action@v3
41+
uses: docker/build-push-action@v5
4042
with:
4143
context: .
4244
push: true

Dockerfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM node:20 AS build
2+
3+
ENV NODE_ENV=production
4+
5+
WORKDIR /usr/src/app
6+
COPY . ./
7+
8+
RUN yarn cache clean
9+
RUN yarn add
10+
11+
CMD ["sh", "server/server.sh"]

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"bootstrap": "yarn",
1414
"dev": "yarn run server:dev",
1515
"build": "vite build",
16-
"server": "node ./server/index.js",
16+
"server": "node --env-file=.env ./server/index.js",
1717
"server:dev": "yarn run server",
1818
"server:prod": "cross-env NODE_ENV=production npm run server"
1919
},
@@ -47,10 +47,12 @@
4747
"dependencies": {
4848
"@blueprintjs/core": "^4.14.1",
4949
"@blueprintjs/select": "4",
50+
"@blueprintjs/table": "^4",
5051
"@lagunovsky/redux-react-router": "^3.2.0",
5152
"@loadable/component": "^5.14.1",
5253
"@macrostrat-web/data-sheet-test": "workspace:*",
5354
"@macrostrat-web/globe": "workspace:*",
55+
"@macrostrat-web/security": "workspace:*",
5456
"@macrostrat/api-utils": "workspace:*",
5557
"@macrostrat/api-views": "workspace:*",
5658
"@macrostrat/column-components": "workspace:*",
@@ -85,6 +87,7 @@
8587
"chroma-js": "^2.4.2",
8688
"classnames": "^2.2.6",
8789
"compression": "^1.7.4",
90+
"cookie-parser": "^1.4.6",
8891
"cross-env": "^7.0.3",
8992
"d3-array": "^3.1.1",
9093
"d3-axis": "^3.0.0",
@@ -97,6 +100,7 @@
97100
"express": "^4.18.2",
98101
"history": "^5.3.0",
99102
"immutability-helper": "^3.1.1",
103+
"jose": "^5.1.2",
100104
"mapbox-gl": "^2.15.0",
101105
"new-github-issue-url": "^1.0.0",
102106
"pbf": "^3.2.1",
@@ -119,9 +123,10 @@
119123
"topojson-client": "^3.0.0",
120124
"transition-hook": "^1.5.2",
121125
"ts-node": "^10.9.1",
126+
"use-debounce": "^9.0.4",
122127
"use-react-router-breadcrumbs": "^3.2.1",
123128
"use-resize-observer": "^9.1.0",
124-
"vike": "^0.4.150",
129+
"vike": "0.4.150-commit-63b1c32",
125130
"vite": "^4.4.9",
126131
"vite-plugin-cesium": "^1.2.22"
127132
},

packages/globe-dev/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import Map from "./map-comparison";
2525
import {
2626
getMapPositionForHash,
2727
applyMapPositionToHash,
28-
} from "/Users/Daven/Projects/Macrostrat/Software/web/src/pages/map/map-interface/app-state/reducers/hash-string";
28+
} from "../../../src/pages/map/map-interface/app-state/reducers/hash-string";
2929

3030
function VisControl({ show, setShown, name }) {
3131
const className = show ? "active" : "";

packages/security/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "@macrostrat-web/security",
3+
"private": true,
4+
"main": "src/index.ts",
5+
"license": "ISC"
6+
}

packages/security/src/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
// Handles fetch requests that require authentication
3+
export const secureFetch = async (url, options) => {
4+
5+
options = {
6+
credentials: "include",
7+
...options,
8+
}
9+
10+
const response = await fetch(url, options);
11+
12+
if (response.status === 401 || response.status === 403) {
13+
14+
const url = new URL(`${import.meta.env.VITE_MACROSTRAT_INGEST_API}/security/login`);
15+
url.searchParams.append("return_url", `${window.location.origin}/dev/security/endpoint`);
16+
17+
window.open(url, '_blank').focus();
18+
throw {name: "UnauthorizedError", message: "User is not logged in"}
19+
}
20+
21+
return response
22+
}

server/index.js

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,24 @@
44
// - To use your environment variables defined in your .env files, you need to install dotenv, see https://vike.dev/env
55
// - To use your path aliases defined in your vite.config.js, you need to tell Node.js about them, see https://vike.dev/path-aliases
66

7-
import express from "express";
8-
import compression from "compression";
7+
import express from "express"
8+
import compression from "compression"
99
import { renderPage } from "vike/server";
10-
import { root } from "./root.js";
11-
const isProduction = process.env.NODE_ENV === "production";
10+
import { root } from "./root.js"
11+
12+
// Auth imports
13+
import cookieParser from "cookie-parser"
14+
import * as jose from "jose"
15+
16+
const isProduction = process.env.NODE_ENV === "production"
1217

1318
startServer();
1419

1520
async function startServer() {
1621
const app = express();
1722

1823
app.use(compression());
24+
app.use(cookieParser());
1925

2026
// Vite integration
2127
if (isProduction) {
@@ -43,9 +49,27 @@ async function startServer() {
4349

4450
// vike middleware. It should always be our last middleware (because it's a
4551
// catch-all middleware superseding any middleware placed after it).
46-
app.get("*", async (req, res, next) => {
52+
app.get('*', async (req, res, next) => {
53+
54+
// Pull out the authorization cookie and decrypt it
55+
let user = undefined
56+
57+
try {
58+
const authHeader = req.cookies?.Authorization
59+
const secret = new TextEncoder().encode(
60+
process.env.SECRET_KEY
61+
);
62+
const jwt = authHeader.substring(7, authHeader.length)
63+
user = (await jose.jwtVerify(jwt, secret)).payload
64+
65+
66+
} catch (e) {
67+
// I don't care if it fails, it just means the user isn't logged in
68+
}
69+
4770
const pageContextInit = {
4871
urlOriginal: req.originalUrl,
72+
user: user
4973
};
5074

5175
const pageContext = await renderPage(pageContextInit);

src/components/map-navbar/index.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,16 @@ export function ParentRouteButton({
2020
return h(LinkButton, { to: "..", icon, minimal: true, ...rest });
2121
}
2222

23-
export function MapNavbar({ title, isOpen, setOpen, parentRoute }) {
23+
export function MapNavbar({
24+
title,
25+
isOpen,
26+
setOpen,
27+
parentRoute,
28+
minimal = false,
29+
}) {
30+
if (minimal) {
31+
return MapMinimalNavbar({ isOpen, setOpen });
32+
}
2433
const { isLoading } = useMapStatus();
2534
return h(FloatingNavbar, { className: "searchbar map-navbar" }, [
2635
h([h(ParentRouteButton, { parentRoute }), h("h2.map-title", title)]),
@@ -32,3 +41,17 @@ export function MapNavbar({ title, isOpen, setOpen, parentRoute }) {
3241
}),
3342
]);
3443
}
44+
45+
function MapMinimalNavbar({ isOpen, setOpen }) {
46+
const { isLoading } = useMapStatus();
47+
return h("div.map-minimal-navbar map-navbar", [
48+
h(FloatingNavbar, { className: "searchbar" }, [
49+
h(MapLoadingButton, {
50+
active: isOpen,
51+
onClick: () => setOpen(!isOpen),
52+
isLoading,
53+
}),
54+
]),
55+
h("div.spacer"),
56+
]);
57+
}

src/components/map-navbar/main.module.sass

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,11 @@
1212
.map-title
1313
// Allow the title to wrap on hover
1414
overflow-x: visible
15+
16+
.map-minimal-navbar
17+
display: flex
18+
flex-direction: row
19+
&>.spacer
20+
flex-grow: 1
21+
.map-navbar
22+
min-width: 0
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {default as h} from "@macrostrat/hyper";
2+
3+
export function Page() {
4+
5+
return h("div", [
6+
h("span", "You are logged in, you can now close this tab.")
7+
]);
8+
}
9+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { render, redirect } from 'vike/abort'
2+
3+
export const guard = (pageContext) => {
4+
const { user } = pageContext
5+
6+
console.log("User: ", user)
7+
8+
if (user === undefined) {
9+
// Render the login page while preserving the URL. (This is novel technique
10+
// which we explain down below.)
11+
throw redirect(`${import.meta.env.VITE_MACROSTRAT_INGEST_API}/security/login?return_url=${pageContext.url}`)
12+
/* The more traditional way, redirect the user:
13+
throw redirect('/login')
14+
*/
15+
}
16+
if (!user.groups.includes(1)) {
17+
// Render the error page and show message to the user
18+
return render(403, 'Only admins are allowed to access this page.')
19+
}
20+
}

src/pages/dev/security/index.page.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {default as h} from "@macrostrat/hyper";
2+
3+
export function Page() {
4+
5+
return h("div", [
6+
h("h1", "Secure Page")
7+
]);
8+
}
9+

src/pages/map/map-interface/app-state/reducers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export default appReducer;
145145
export * from "./core";
146146
export * from "./map";
147147
export * from "./types";
148+
export * from "./hash-string";
148149

149150
/*
150151
function overallReducer(state: AppState, action: Action): AppState {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
3+
import {EditableCell2, EditableCell2Props} from "@blueprintjs/table";
4+
5+
import hyper from "@macrostrat/hyper";
6+
import styles from "./main.module.sass";
7+
import { getTableUpdate } from "~/pages/maps/@id/edit/table-util";
8+
9+
export const h = hyper.styled(styles);
10+
11+
12+
interface EditableCell extends EditableCell2Props {
13+
columnName: string,
14+
rowIndex: number
15+
}
16+
17+
export const EditableCell = ({...props}: EditableCell2Props) => {
18+
19+
return h(EditableCell2, {...props});
20+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {Button, MenuItem} from "@blueprintjs/core";
2+
import {Select2, ItemRenderer} from "@blueprintjs/select";
3+
import {EditableCell2Props} from "@blueprintjs/table";
4+
import React, {useEffect, useMemo} from "react";
5+
6+
// @ts-ignore
7+
import hyper from "@macrostrat/hyper";
8+
9+
import "@blueprintjs/core/lib/css/blueprint.css"
10+
import "@blueprintjs/select/lib/css/blueprint-select.css";
11+
import styles from "../../edit-table.module.sass";
12+
13+
14+
const h = hyper.styled(styles);
15+
16+
17+
export const EditorPopover = () => {
18+
19+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
interface CellProps extends React.HTMLProps<HTMLTableCellElement> {
2+
value: string;
3+
onChange: (value: string) => void;
4+
}

0 commit comments

Comments
 (0)