Skip to content
Draft
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
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ node_modules
.next
out
public
bak
bak
src/db/server/*
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
}
]
},

"parser": "@typescript-eslint/parser",
"overrides": [
{
Expand Down
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
node_modules
/.pnp
.pnp.js

Expand Down Expand Up @@ -36,4 +36,8 @@ yarn-error.log*

test-results/
results.xml
unit-tests.xml
unit-tests.xml

**/*.js.map
**/*.css.map
temp
20 changes: 20 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need some docs on how to run

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing to document. If you have a reason to run the DB application in debug mode, use the DB App launch config, otherwise don't...

"name": "DB App",
"program": "${workspaceFolder}/src/db/server/app.js",
"args": ["--port=11013", "--test"],
"request": "launch",
"skipFiles": [
"<node_internals>/**"
],
"type": "node"
},
{
"type": "node",
"request": "launch",
Expand All @@ -21,6 +31,16 @@
"NODE_OPTIONS": "--inspect"
}
},
{
"name": "Next.js: debug with indexing",
"type": "node-terminal",
"request": "launch",
"command": "yarn dev-index",
"env": {
"NODE_OPTIONS": "--inspect",
"ML_INDEX_CONTENT": "true"
}
},
{
"type": "node",
"request": "attach",
Expand Down
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,18 @@
"clean": "rm -rf .next",
"dev": "next dev",
"build": "next build",
"prebuild-index": "bash scripts/run-indexer.sh -P 20123",

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need some docs on how to run

@imdfl imdfl Mar 11, 2023

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, pre- and post- tasks are run automatically. yarn build-index will run prebuild-index before and postbuild-index after.

"build-index": "env ML_INDEX_CONTENT=true ML_DB_API_PORT=20123 next build",
"postbuild-index": "bash scripts/dump-and-stop-indexer.sh -O public/full-db.json -P 20123",
"build:sitemap": "next-sitemap",
"start": "next start",
"lint": "eslint . -f codeframe",
"prettier:check": "npx prettier --config .prettierrc --ignore-path .prettierignore --check src",
"test:e2e": "playwright test",
"test:unit": "vitest __tests__",
"test:unit:ci": "vitest __tests__ --reporter=junit --outputFile.junit=unit-tests-results.xml"
"test:unit:ci": "vitest __tests__ --reporter=junit --outputFile.junit=unit-tests-results.xml",
"dev-index": "next dev",
"predev-index": "bash scripts/run-indexer.sh"
},
"dependencies": {
"@radix-ui/react-icons": "^1.1.1",
Expand All @@ -61,6 +66,9 @@
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "18.2.0",
"react-google-recaptcha": "^2.1.0",
"rimraf": "^3.0.2",
"rxdb": "^14.1.8",
"rxjs": "^7.8.0",
"tmp": "^0.2.1"
},
"devDependencies": {
Expand All @@ -76,6 +84,7 @@
"@types/js-cookie": "^3.0.2",
"@types/react": "^18.0.26",
"@types/react-google-recaptcha": "^2.1.5",
"@types/rimraf": "^3.0.2",
"@types/tmp": "^0.2.3",
"@typescript-eslint/eslint-plugin": "^5.45.1",
"@typescript-eslint/parser": "^5.45.1",
Expand Down
1 change: 1 addition & 0 deletions public/full-db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schemas":[{"articles":{"schema":{"version":0,"title":"Schema for ML Articles","keyCompression":false,"primaryKey":"url","type":"object","properties":{"url":{"type":"string","maxLength":100},"labels":{"type":"array","items":{"type":"string"}},"startDate":{"type":"number"},"endDate":{"type":"number"},"locales":{"type":"array","items":{"type":"string"}}},"required":["url","labels","locales","startDate"]}}}],"data":{"name":"ml","instanceToken":"morvycskkp","collections":[{"name":"articles","schemaHash":"dyk9bi","docs":[{"url":"about","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718891.07},"_deleted":false},{"url":"contribute","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718931.02},"_deleted":false},{"url":"docs","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718594.01},"_deleted":false},{"url":"docs/the-story-of-mel","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718600.01},"_deleted":false},{"url":"docs/the-story-of-mel/codex","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718642.07},"_deleted":false},{"url":"docs/the-story-of-mel/pages/blackjack-writeup","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134716878.01},"_deleted":false},{"url":"docs/the-story-of-mel/pages/mels-hack-the-missing-bits","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134717242.01},"_deleted":false},{"url":"docs/the-story-of-mel/pages/preface","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134717372.02},"_deleted":false},{"url":"docs/the-story-of-mel/pages/resources","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134717399.06},"_deleted":false},{"url":"glossary","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718608.05},"_deleted":false},{"url":"glossary/addressing-scheme","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718640.01},"_deleted":false},{"url":"glossary/assembly-language","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718645.07},"_deleted":false},{"url":"glossary/bit","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718649.07},"_deleted":false},{"url":"glossary/compiler","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718654.07},"_deleted":false},{"url":"glossary/drum-memory","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718666.01},"_deleted":false},{"url":"glossary/fortran","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718670.06},"_deleted":false},{"url":"glossary/friden-flexowriter","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718682.07},"_deleted":false},{"url":"glossary/goto","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718686.07},"_deleted":false},{"url":"glossary/hexadecimal","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718691.07},"_deleted":false},{"url":"glossary/infinite-loop","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718695.01},"_deleted":false},{"url":"glossary/jump-instruction","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718701.07},"_deleted":false},{"url":"glossary/lgp-30","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718705.07},"_deleted":false},{"url":"glossary/loop","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718709.07},"_deleted":false},{"url":"glossary/machine-code","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718713.07},"_deleted":false},{"url":"glossary/magnetic-core-memory","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718722.07},"_deleted":false},{"url":"glossary/operand","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718728.02},"_deleted":false},{"url":"glossary/operation-code","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718734.07},"_deleted":false},{"url":"glossary/optimal-code","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718739.01},"_deleted":false},{"url":"glossary/optimum","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718743.01},"_deleted":false},{"url":"glossary/pascal","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718748.03},"_deleted":false},{"url":"glossary/pessimum","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718762.02},"_deleted":false},{"url":"glossary/port","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718772.04},"_deleted":false},{"url":"glossary/ratfor","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718776.07},"_deleted":false},{"url":"glossary/real-programmer","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718785.01},"_deleted":false},{"url":"glossary/register","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718788.07},"_deleted":false},{"url":"glossary/rpc-4000","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718804.02},"_deleted":false},{"url":"glossary/terminating-condition","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718809.01},"_deleted":false},{"url":"glossary/test-terminating-condition","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718817.07},"_deleted":false},{"url":"glossary/time-delay-loop","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718821.01},"_deleted":false},{"url":"glossary/top-down-design","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718823.07},"_deleted":false},{"url":"glossary/vacuum-tube","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718836.07},"_deleted":false},{"url":"posts/07-04-21-hebrew-edition","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718954.06},"_deleted":false},{"url":"posts/14-01-21-here-we-go","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718958.02},"_deleted":false},{"url":"posts/21-05-22-project-launch","labels":[],"locales":["en","he"],"_meta":{"lwt":1678134718961.07},"_deleted":false}]}]}}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NICE!

62 changes: 62 additions & 0 deletions scripts/dump-and-stop-indexer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/bin/bash

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need docs on how to run

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(see below)


OUTFILE=""
DB_PORT=""
USAGE="Usage: $( basename $0 ) -O </full/out/file/path> -P <port of DB server>"

shopt -s nocasematch
while (( "$#" )); do
nextarg=$1
shift
case $nextarg in
"-O"|"--outfile")
OUTFILE="$1"
shift
;;
"-P"|"--port")
DB_PORT="$1"
shift
;;
*)
echo $USAGE
echo "unkonwn option $nextarg" >&2
exit 1
;;
esac
done

if [ $DB_PORT == "" ] ; then
echo $USAGE >& 2
exit 1
fi

if [ "$OUTFILE" == "" ]; then
echo $USAGE >& 2
exit 1
fi

OUTPATH=`dirname "$OUTFILE"`
OUTFILE=`basename "$OUTFILE"`
# ensure out path exists
mkdir -p "$OUTPATH"

if [ ! -d "$OUTPATH" ]; then
echo "Failed to create folder $OUTPATH"
exit 1;
fi

DB_URL="http://localhost:${DB_PORT}"

# get full outfile path (param may be relative)
OUTPATH=$( cd "$OUTPATH" && pwd -P )

ENCODED_PATH=$( node -p "encodeURIComponent('$OUTPATH/$OUTFILE')" )

#dump db to file
curl "${DB_URL}/save/$ENCODED_PATH"
#and terminate the db server
curl ${DB_URL}/terminate

# Add the result to git
git add "$OUTPATH/$OUTFILE"

37 changes: 37 additions & 0 deletions scripts/run-indexer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/bash

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, need docs on how to run

@imdfl imdfl Mar 11, 2023

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomerlichtash
(also relevant to other script(s))

  1. You don't need to run the scripts, they're not standalone. You only run yarn build-index (or yarn dev-index).
  2. The scripts are documented and print a usage message when run with no params. bash <script>.

# get dir of running script
SCRIPT_DIR=`dirname "$0"`;

DB_PORT=""
USAGE="Usage: $( basename $0 ) -P <port of DB server>"

shopt -s nocasematch
while (( "$#" )); do
nextarg=$1
shift
case $nextarg in
"-P"|"--port")
DB_PORT="$1"
shift
;;
*)
echo $USAGE
echo "unkonwn option $nextarg" >&2
exit 1
;;
esac
done

if [ $DB_PORT == "" ] ; then
echo $USAGE >& 2
exit 1
fi

#move to a known folder
pushd "$SCRIPT_DIR"
#cd to db server
cd ../src/db/server
#run the server
node app.js --port=$DB_PORT &
# node app.js > /dev/null &
popd
4 changes: 2 additions & 2 deletions src/components/layout/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Analytics from "./analytics";
import { ComponentProps } from "../../interfaces/models";
import { FavIconAnimator, IFavIconProps } from "../../lib/favicon-animator";
import { st, classes } from "./layout.st.css";
import { mlUtils } from "../../lib/ml-utils";

export interface ILayoutProps extends ComponentProps {
title?: string;
Expand All @@ -27,7 +28,6 @@ const ICON_ANIMATOR_PROPS: IFavIconProps = {
image: "/assets/ml-logo.png",
};

const isDebug = process.env.NEXT_PUBLIC_ML_DEBUG;

export default function Layout({ children, title }: ILayoutProps) {
const router = useRouter();
Expand Down Expand Up @@ -97,7 +97,7 @@ export default function Layout({ children, title }: ILayoutProps) {
</ScrollArea>
{isMobile && <MenuProvider isMobile />}
</div>
{!isDebug && <Analytics />}
{!mlUtils.appEnvironment.isDevMode && <Analytics />}
</>
);
}
29 changes: 19 additions & 10 deletions src/contexts/page-context.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import React, { Context, createContext } from "react";
import { createDBService } from "../db/client/client-db-service";
import { IClientDBService } from "../interfaces/db-service.d";
import { IDynamicContentServer } from "../interfaces/dynamic-content";
import { IPageContext } from "../interfaces/page-context";
import { DynamicContentServer } from "../lib/dynamic-content-server";

export class PageContext implements IPageContext {
class PageContext implements IPageContext {
constructor(
public readonly dynamicContentServer: IDynamicContentServer,
public readonly dbService: IClientDBService,
public documentPath: string
) {}
) {
}
}

const ctx = createContext<IPageContext>(new PageContext(null, ""));
const ctx = createContext<IPageContext>(new PageContext(null, null, ""));

export const ReactPageContext: Context<IPageContext> = ctx;

export const PageContextProvider = ({ documentPath, children }) => (
<ReactPageContext.Provider
value={new PageContext(new DynamicContentServer(), documentPath as string)}
>
{children}
</ReactPageContext.Provider>
);
export const PageContextProvider = ({ documentPath, children }) => {
return (
<ReactPageContext.Provider
value={new PageContext(
new DynamicContentServer(),
createDBService(),
documentPath as string)}
>
{children}
</ReactPageContext.Provider>
);
}
101 changes: 101 additions & 0 deletions src/db/client/client-db-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/* eslint @typescript-eslint/no-explicit-any: 0 */

// Some RxDB types are either complicated or obscure enough to justify
// casting some parameters as any

import { RxDBDevModePlugin } from 'rxdb/plugins/dev-mode';
import { RxDBJsonDumpPlugin } from 'rxdb/plugins/json-dump';
import { createRxDatabase, RxDatabase, addRxPlugin } from 'rxdb';
import { getRxStorageMemory } from "rxdb/plugins/storage-memory";

import { IClientDBService } from "../../interfaces/db-service.d";
import { DB_SERVICE_MODELS } from "../models/index.d";
import { importDBData } from '../common/db-utils';
import { mlUtils } from '../../lib/ml-utils';

enum DBStates {
None, Loading, Loaded, Error
}

class ClientDBService implements IClientDBService {
private _db: RxDatabase;
private _state = DBStates.None;

constructor() {
if (mlUtils.appEnvironment.isBrowser) {
addRxPlugin(RxDBDevModePlugin);
addRxPlugin(RxDBJsonDumpPlugin);
}
}

public async load(): Promise<string> {
if (this._state === DBStates.Loading) {
return"service already loading";
}
if (this._state === DBStates.Loaded) {
return "";
}
if (!mlUtils.appEnvironment.isBrowser) {
this._state = DBStates.Loaded;
return "";
}
this._state = DBStates.Loading;
let error = "";
try {
const url = `/full-db.json`;
const response = await fetch(url, {
method: "GET",
});
const responseData = await response.json() as DB_SERVICE_MODELS.IDBDump;
if (responseData?.data && responseData.schemas) {
error = await this.createDB(responseData);
}
else {
error = "Failed to get or parse DB data";
}
}
catch (e) {
error = String(e);
}
this._state = error ? DBStates.Error : DBStates.Loaded;

return error ? `Error initializing database ${error}` : "";
}


private async createDB(dump: DB_SERVICE_MODELS.IDBDump): Promise<string> {
try {
const db = await createRxDatabase({
name: "db-service",
storage: getRxStorageMemory()
});
for (let schema of dump.schemas || []) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const added = await db.addCollections(schema as any);
console.log(added);
}
await importDBData( { db, dump, loadData: true });
// const importData = this.processJSON(db, dump.data);
// // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
// await db.importJSON(importData as any);
const all = await db.collections.articles.find().exec();
console.log(all);

this._db = db;
return "";
}
catch (e) {
return String(e);
}
}
}

let globalDB: IClientDBService | null = null;

export const createDBService: () => IClientDBService = () => {
if (!globalDB) {
globalDB = new ClientDBService();
}

return globalDB;
}
Loading