refactor: remove useless code from starter project + yarn add fastify-git-server
+ 150
- 574
@@ -1,4 +1,5 @@
-# COOKIE_NAME="fake"
-# DEPLOYMENT_DOMAIN="fake"
-# DEPLOYMENT_SCHEME="fake"
-# COOKIE_SECRET="fake"
+# COOKIE_NAME='fake'
+# COOKIE_SECRET='fake'
+# DEPLOYMENT_DOMAIN='fake'
+# DEPLOYMENT_SCHEME='fake'
+# GIT_REPOSITORIES_ROOT='fake'

@@ -1,14 +1,8 @@
 {
-  "_generatedAtUnix": 1662851607522,
+  "_generatedAtUnix": 1662913620980,
   "_hashAlgorithm": "sha1",
   "_version": 2,
   "islands": {
-    "Counter": {
-      "hash": "1a47f269e78e467e9f5ed803e037d4ee3deccfe2",
-      "pathSource": "./app/islands/Counter.tsx",
-      "pathBundle": "./public/.islands/Counter.bundle.js",
-      "pathSourceMap": "./public/.islands/Counter.bundle.js.map"
-    },
     "InstantRouterIndicator": {
       "hash": "89c6f145dfed92e98a03b22596fed9c852792ee9",
       "pathSource": "./app/islands/InstantRouterIndicator.tsx",

...
@@ -24,7 +18,7 @@
   },
   "views": {
     "DocHomeView": {
-      "hash": "c4b04c9c09d88ea32bf7595c1a105e168d07c660",
+      "hash": "82b1841cde408e5bb5f85b457d58185f46a27263",
       "pathSource": "./app/views/DocHomeView.tsx"
     },
     "DocPageView": {

...
@@ -32,7 +26,7 @@
       "pathSource": "./app/views/DocPageView.tsx"
     },
     "HomeView": {
-      "hash": "3a4d7b139d89afe9274ebd2f3ecf0a6f010e8c94",
+      "hash": "220c689add1c039287faae6e7d2b678c13a8d0ba",
       "pathSource": "./app/views/HomeView.tsx"
     },
     "InternalErrorView": {

file deleted
app/components/DocSectionEntryCard.tsx
@@ -1,129 +0,0 @@
-import React, { VFC } from "react";
-import styled, { css } from "styled-components";
-
-import type { AppThemeScheme, WithThemeSchemeProp } from "../types";
-import { Card } from "./Card.styled";
-import { ChevronRightIcon } from "./icons";
-import { NamedColors } from "../utils/style";
-
-interface DocSectionEntryCardProps {
-  coverImageUrl: string;
-  href: string;
-  summary: string;
-  themeScheme: AppThemeScheme;
-  title: string;
-}
-
-export const DocSectionEntryCard: VFC<DocSectionEntryCardProps> = ({
-  coverImageUrl,
-  href,
-  summary,
-  themeScheme,
-  title,
-}) => {
-  return (
-    <StyledDocSectionEntryCard themeScheme={themeScheme}>
-      <StyledDocSectionEntryLink href={href} title={title}>
-        <StyledCoverImage src={coverImageUrl} themeScheme={themeScheme} />
-        <StyledTitleRow themeScheme={themeScheme}>
-          <h2>{title}</h2>
-          <ChevronRightIcon />
-        </StyledTitleRow>
-        <StyledSummaryText themeScheme={themeScheme}>
-          {summary}
-        </StyledSummaryText>
-      </StyledDocSectionEntryLink>
-    </StyledDocSectionEntryCard>
-  );
-};
-
-const StyledDocSectionEntryCard = styled(Card)`
-  flex: 1;
-  min-width: 350px;
-  max-width: 100%;
-  min-height: 395px;
-
-  transition: transform 180ms ease-in-out 0s;
-
-  &:hover {
-    transform: translateY(-6px);
-
-    & h2 {
-      text-decoration: underline;
-    }
-  }
-`;
-
-const StyledDocSectionEntryLink = styled.a`
-  height: 100%;
-  width: 100%;
-
-  text-decoration: unset;
-  color: unset;
-`;
-
-const StyledCoverImage = styled.div<WithThemeSchemeProp & { src: string }>`
-  width: 100%;
-  min-height: 175px;
-  height: 175px;
-  max-height: 175px;
-
-  ${({ src, themeScheme }) => css`
-    background-color: ${NamedColors.CARD_OVERLAY[themeScheme]};
-    background-image: url("${src}");
-    background-position: center center;
-    background-repeat: no-repeat;
-    background-size: cover;
-  `};
-
-  appearance: none;
-  border-image: none;
-  border: none;
-  border-color: transparent;
-  border-radius: 8px;
-  outline: none;
-  overflow: hidden;
-`;
-
-const StyledTitleRow = styled.div<WithThemeSchemeProp>`
-  display: flex;
-  flex-flow: row nowrap;
-  justify-content: center;
-  align-items: center;
-
-  margin-top: 20px;
-
-  & > h2 {
-    flex: 1;
-    margin: 0;
-  }
-
-  & > div[aria-roledescription="icon"] {
-    display: flex;
-    flex-flow: row nowrap;
-    justify-content: center;
-    align-items: center;
-
-    min-width: 32px;
-    width: 32px;
-    max-width: 32px;
-    min-height: 32px;
-    height: 32px;
-    max-height: 32px;
-
-    ${({ themeScheme }) => css`
-      background-color: ${NamedColors.CARD_OVERLAY[themeScheme]};
-      color: ${NamedColors.TEXT_DEFAULT[themeScheme]};
-    `};
-
-    border-radius: ${32 / 2}px;
-  }
-`;
-
-const StyledSummaryText = styled.p<WithThemeSchemeProp>`
-  line-height: 24px;
-
-  ${({ themeScheme }) => css`
-    color: ${NamedColors.TEXT_MUTED[themeScheme]};
-  `};
-`;

app/components/MarkdownToJsx.tsx
@@ -7,8 +7,6 @@ import type { AppThemeScheme, WithThemeSchemeProp } from "../types";
 import { Card } from "./Card.styled";
 import { NamedColors } from "../utils/style";
 
-import Counter from "../islands/Counter";
-
 type MarkdownToJsxProps = {
   themeScheme: AppThemeScheme;
   markdown: string;

...
@@ -74,9 +72,6 @@ export const MarkdownToJsx: VFC<MarkdownToJsxProps> = ({
               {children}
             </li>
           ),
-          Counter: ({ defaultValue = 0 }) => (
-            <Counter defaultValue={parseInt(defaultValue, 10)} />
-          ),
           ImageWithTheme: ({
             alt,
             lightSrc,

app/components/PageHeader.tsx
@@ -3,14 +3,13 @@ import React, { VFC } from "react";
 import styled, { css } from "styled-components";
 // app
 import type { WithThemeSchemeProp } from "../types";
+import { Const } from "../const";
 import { NamedColors } from "../utils/style";
-import { ReactMonolithLogo } from "./icons";
+// import { ReactMonolithLogo } from "./icons";
 
-interface PageHeaderProps {
-  currentModule?: "documentation" | "api-reference" | "blueprints";
-}
+interface PageHeaderProps {}
 
-const REACT_MONOLITH_LOGO_SIZE = 36;
+// const LOGO_HEIGHT = 36;
 const SIDE_MENU_WIDTH = 320;
 
 export const PageHeader: VFC<PageHeaderProps & WithThemeSchemeProp> = ({

...
@@ -21,25 +20,13 @@ export const PageHeader: VFC<PageHeaderProps & WithThemeSchemeProp> = ({
     <StyledPageHeader themeScheme={themeScheme}>
       <StyledLogoArea themeScheme={themeScheme}>
         <a href={"/"}>
-          <ReactMonolithLogo size={REACT_MONOLITH_LOGO_SIZE} />
-          <h1 style={{ margin: 0, marginLeft: 20 }}>React Monolith</h1>
+          {/*<ReactMonolithLogo size={LOGO_HEIGHT} />*/}
+          <h1 style={{ margin: 0, marginLeft: 20 }}>{Const.APP_NAME}</h1>
         </a>
       </StyledLogoArea>
       <StyledPageHeaderNav>
-        <a aria-label={"Documentation"} href={"/docs"}>
-          Documentation
-        </a>
-        <a aria-label={"API Reference"} href={"/api-reference"}>
-          API Reference
-        </a>
-        <a aria-label={"Blueprints"} href={"/blueprints"}>
-          Blueprints
-        </a>
-        <a aria-label={"Playground"} href={"/playground"}>
-          Playground
-        </a>
-        <a aria-label={"Contribute"} href={"/contribute"}>
-          Contribute
+        <a aria-label={"Explore Projects"} href={"/explore/projects"}>
+          Explore Projects
         </a>
       </StyledPageHeaderNav>
       <StyledActionsArea>

...
@@ -52,6 +39,12 @@ export const PageHeader: VFC<PageHeaderProps & WithThemeSchemeProp> = ({
         >
           {`${themeScheme === "light" ? "Dark" : "Light"} mode`}
         </a>
+        <a aria-label={"Register a new account"} href={"/auth/register"}>
+          Register
+        </a>
+        <a aria-label={"Login to your account"} href={"/auth/login"}>
+          Login
+        </a>
       </StyledActionsArea>
     </StyledPageHeader>
   );

app/components/index.ts
@@ -1,6 +1,6 @@
 export { Button, ButtonAnchor } from "./Button.styled";
 export { Card } from "./Card.styled";
-export { DocSectionEntryCard } from "./DocSectionEntryCard";
+export * as Icons from "./icons";
 export { Layout } from "./Layout";
 export { MarkdownToJsx } from "./MarkdownToJsx";
 export { MenuItem } from "./MenuItem";

@@ -1,3 +1,5 @@
+// beware to imports in this file, they must work in both server/client sides.
+
 import type { AppThemeScheme, SectionsPagesIndex } from "./types";
 
 type Const = {

...
@@ -10,7 +12,7 @@ type Const = {
 };
 
 export const Const: Const = {
-  APP_NAME: "React Monolith",
+  APP_NAME: "GitFOSS",
   DEFAULT_THEME_SCHEME: "dark",
   SSR_CACHE_MAX_SIZE_BYTES: 50_000_000, // 50Mb in bytes
   // TODO(refactor): all of this will move into folder/index.yml

file deleted
app/controllers/DocumentationController.ts
@@ -1,140 +0,0 @@
-// 1st-party
-import { HeadTag } from "@ethicdevs/fastify-stream-react-views";
-import { ReqHandler } from "@ethicdevs/react-monolith";
-// app
-import type { Page, SectionsWithPages } from "../types";
-import { Const } from "../const";
-import { Env } from "../env";
-import { getDocFileContent } from "../utils/server";
-import { makeSectionsService } from "../services/SectionsService";
-
-import DocHomeView, { DocHomeViewProps } from "../views/DocHomeView";
-import DocPageView, { DocPageViewProps } from "../views/DocPageView";
-
-export const getDocHomeView: ReqHandler = async (request, reply) => {
-  const reqHandler = reply.makeRequestHandler(request, reply);
-  return reqHandler<DocHomeViewProps>(DocHomeView.name, {
-    title: "Documentation",
-  });
-};
-
-export const reloadDocsEndpoint: ReqHandler = async (request, reply) => {
-  // Reload content data in memory
-  const sectionsWithPages: SectionsWithPages = await Object.entries(
-    Const.SECTIONS_WITH_PAGES
-  ).reduce(async (accP, [sectionSlug, sectionPagesSlugs]) => {
-    let acc = await accP;
-    const pageForSection = await sectionPagesSlugs.reduce(
-      async (accP, pageSlug) => {
-        const acc = await accP;
-        const page = await getDocFileContent(sectionSlug, pageSlug, "md");
-        return {
-          ...acc,
-          [pageSlug]: page,
-        };
-      },
-      Promise.resolve({} as { [pageSlug: string]: Page })
-    );
-    acc = {
-      ...acc,
-      [sectionSlug]: {
-        slug: sectionSlug,
-        title: Const.SECTIONS_TITLES[sectionSlug],
-        summary: Const.SECTIONS_SUMMARIES[sectionSlug],
-        pagesBySlug: pageForSection,
-      },
-    };
-    return acc;
-  }, Promise.resolve({} as SectionsWithPages));
-
-  // (for the setter definition @see server.ts)
-  request.sectionsWithPages = sectionsWithPages;
-
-  return reply.send("OK");
-};
-
-export const getSectionEntrypoint: ReqHandler = async (request, reply) => {
-  const { sectionSlug } = request.params as { sectionSlug: string };
-
-  const sectionsService = await makeSectionsService(request);
-  const section = await sectionsService.getSectionBySlug(sectionSlug);
-  const firstPage = await sectionsService.getFirstPage(section);
-
-  if (firstPage == null) {
-    return reply.redirect(302, `/docs`);
-  }
-
-  return reply.redirect(302, `/docs/${section.slug}/${firstPage.slug}`);
-};
-
-export const getSectionPageView: ReqHandler = async (request, reply) => {
-  const { sectionSlug, pageSlug } = request.params as {
-    sectionSlug: string;
-    pageSlug: string;
-  };
-
-  if (pageSlug == null || pageSlug.trim() === "") {
-    return reply.redirect(307, `/docs/${sectionSlug}`);
-  }
-
-  const sectionsService = await makeSectionsService(request);
-  const section = await sectionsService.getSectionBySlug(sectionSlug);
-
-  if (
-    section == null ||
-    pageSlug in section.pagesBySlug === false ||
-    section.pagesBySlug[pageSlug] == null
-  ) {
-    return reply.callNotFound();
-  }
-
-  const page = section.pagesBySlug[pageSlug];
-
-  const reqHandler = reply.makeRequestHandler(request, reply);
-  return reqHandler<DocPageViewProps>(
-    DocPageView.name,
-    {
-      page,
-      title: `${page.metas?.title} / ${section.title}`,
-    },
-    {
-      head: [
-        page.metas?.title && {
-          kind: "meta",
-          name: "title",
-          content: page.metas?.title,
-        },
-        page.metas?.summary && {
-          kind: "meta",
-          name: "description",
-          content: page.metas?.summary,
-        },
-        {
-          kind: "meta",
-          name: "og:type",
-          content: "article",
-        },
-        {
-          kind: "meta",
-          name: "og:url",
-          content: `${Env.DEPLOYMENT_SCHEME}://${Env.DEPLOYMENT_DOMAIN}/docs/${section.slug}/${page.slug}`,
-        },
-        {
-          kind: "meta",
-          name: "article:section",
-          content: section.title,
-        },
-        page.metas?.title && {
-          kind: "meta",
-          name: "og:title",
-          content: page.metas?.title,
-        },
-        page.metas?.summary && {
-          kind: "meta",
-          name: "og:description",
-          content: page.metas?.summary,
-        },
-      ] as HeadTag[],
-    }
-  );
-};

app/controllers/HomeController.ts
@@ -12,6 +12,8 @@ export const getHomeView: ReqHandler = async (request, reply) => {
   const foo = authService.isExistingUsername("test");
   const user = authService.findUserByUid((request.params as any).uid);
 
+  await authService.validateUserEmailAddress("user-uid", "email@address.tld");
+
   // TODO: Create a User model in db where to store user data.
   user; // so typescript do not complains about unused variable.
 

@@ -1,13 +1,13 @@
 // std
-import fs from "fs";
-import path from "path";
+import { existsSync } from "node:fs";
+import { join, resolve } from "node:path";
 // 3rd-party
 import dotEnvFlow from "dotenv-flow";
 
 import { Env as NodeEnv } from "@ethicdevs/react-monolith";
 
 const NODE_ENV = process.env.NODE_ENV;
-const ENV_FILES_DIR = path.resolve(path.join(__dirname, ".."));
+const ENV_FILES_DIR = resolve(join(__dirname, ".."));
 
 const baseOpts = {
   node_env: NODE_ENV,

...
@@ -16,7 +16,7 @@ const baseOpts = {
 const files = dotEnvFlow.listDotenvFiles(ENV_FILES_DIR, baseOpts);
 
 function getLoadableFilesDisplay(): string {
-  return files.filter((path) => fs.existsSync(path) === true).join("\n  * ");
+  return files.filter((path) => existsSync(path) === true).join("\n  * ");
 }
 
 if (files.length > 0) {

...
@@ -96,13 +96,22 @@ const getCookieSecret = (cookieSecret?: string | null): string => {
   }
   return String(cookieSecret);
 };
+const getGitRepositoriesRoot = (
+  gitRepositoriesRoot?: string | null
+): string => {
+  if (gitRepositoriesRoot == null || gitRepositoriesRoot === "fake") {
+    throw new Error("[env] GIT_REPOSITORIES_ROOT is missing.");
+  }
+  return String(gitRepositoriesRoot);
+};
 
 interface IEnv {
   NODE_ENV: NodeEnv;
   COOKIE_NAME: string;
+  COOKIE_SECRET: string;
   DEPLOYMENT_DOMAIN: string;
   DEPLOYMENT_SCHEME: "http" | "https";
-  COOKIE_SECRET: string;
+  GIT_REPOSITORIES_ROOT: string;
 }
 
 export const Env: IEnv = {

...
@@ -111,4 +120,7 @@ export const Env: IEnv = {
   COOKIE_SECRET: getCookieSecret(process.env.COOKIE_SECRET),
   DEPLOYMENT_DOMAIN: getDeploymentDomain(process.env.DEPLOYMENT_DOMAIN),
   DEPLOYMENT_SCHEME: getDeploymentScheme(process.env.DEPLOYMENT_SCHEME),
+  GIT_REPOSITORIES_ROOT: getGitRepositoriesRoot(
+    process.env.GIT_REPOSITORIES_ROOT
+  ),
 };

file deleted
app/islands/Counter.tsx
@@ -1,58 +0,0 @@
-import React, { CSSProperties, useCallback, useState } from "react";
-import type { ReactIsland } from "@ethicdevs/react-monolith";
-
-// app
-import { Button } from "../components";
-
-interface CounterProps {
-  defaultValue?: number;
-}
-
-const Counter: ReactIsland<CounterProps> = ({ defaultValue = 42 }) => {
-  const [counter, setCounter] = useState(defaultValue);
-  const incrementCounter = useCallback(
-    () => setCounter((prev) => prev + 1),
-    [setCounter]
-  );
-  const decrementCounter = useCallback(
-    () => setCounter((prev) => prev - 1),
-    [setCounter]
-  );
-
-  return (
-    <div style={styles.counterContainer}>
-      <Button
-        onClick={decrementCounter}
-        type={"button"}
-        title={"Click to Decrement"}
-      >
--      </Button>
-      <div style={styles.counterText}>{counter}</div>
-      <Button
-        onClick={incrementCounter}
-        type={"button"}
-        title={"Click to Increment"}
-      >
--      </Button>
-    </div>
-  );
-};
-
-const styles: Record<string, CSSProperties> = {
-  counterContainer: {
-    display: "flex",
-    flexFlow: "row nowrap",
-    justifyContent: "flex-start",
-    alignItems: "center",
-    fontFamily: "monospace",
-  },
-  counterText: {
-    padding: 10,
-    fontSize: 18,
-    fontWeight: "bold",
-  },
-};
-
-export default Counter;

@@ -6,69 +6,32 @@ import React from "react";
 // app
 /* controllers */
 import * as HomeController from "./controllers/HomeController";
-import * as DocumentationController from "./controllers/DocumentationController";
 import * as ThemeController from "./controllers/ThemeController";
 
-export enum DocAppRoute {
+export enum AppRoute {
   HOME = "home",
-  DOCS_HOME = "docs.home",
-  DOCS_MANAGE_MEMORY_RELOAD = "docs.mgmt.memory-reload",
-  DOCS_SECTION_ENTRY = "docs.section.entry",
-  DOCS_SECTION_PAGE = "docs.section.page",
   THEME_SET = "theme.set",
 }
 
-export interface DocAppRoutesParams extends IRouteParams {
-  [DocAppRoute.HOME]: undefined;
-  [DocAppRoute.DOCS_HOME]: undefined;
-  [DocAppRoute.DOCS_SECTION_ENTRY]: {
-    sectionSlug: string;
-  };
-  [DocAppRoute.DOCS_SECTION_PAGE]: {
-    sectionSlug: string;
-    pageSlug: string;
-  };
-  [DocAppRoute.THEME_SET]: {
+export interface AppRoutesParams extends IRouteParams {
+  [AppRoute.HOME]: undefined;
+  [AppRoute.THEME_SET]: {
     themeScheme: string;
   };
 }
 
-const DocAppRouter: AppRouter = () => (
+const RootAppRouter: AppRouter = () => (
   <Router.Root>
     <></>
     <Router.Group type={AppRouterGroup.API}>
       <Router.Route
-        name={DocAppRoute.HOME}
+        name={AppRoute.HOME}
         method={"GET"}
         path={"/"}
         handler={HomeController.getHomeView}
       />
       <Router.Route
-        name={DocAppRoute.DOCS_MANAGE_MEMORY_RELOAD}
-        method={"GET"}
-        path={"/docs/mgmt/memory-reload"}
-        handler={DocumentationController.reloadDocsEndpoint}
-      />
-      <Router.Route
-        name={DocAppRoute.DOCS_HOME}
-        method={"GET"}
-        path={"/docs"}
-        handler={DocumentationController.getDocHomeView}
-      />
-      <Router.Route
-        name={DocAppRoute.DOCS_SECTION_ENTRY}
-        method={"GET"}
-        path={"/docs/:sectionSlug"}
-        handler={DocumentationController.getSectionEntrypoint}
-      />
-      <Router.Route
-        name={DocAppRoute.DOCS_SECTION_PAGE}
-        method={"GET"}
-        path={"/docs/:sectionSlug/:pageSlug"}
-        handler={DocumentationController.getSectionPageView}
-      />
-      <Router.Route
-        name={DocAppRoute.THEME_SET}
+        name={AppRoute.THEME_SET}
         method={"GET"}
         path={"/theme/:themeScheme"}
         handler={ThemeController.getTheme}

...
@@ -77,4 +40,4 @@ const DocAppRouter: AppRouter = () => (
   </Router.Root>
 );
 
-export default DocAppRouter;
+export default RootAppRouter;

@@ -6,6 +6,7 @@ import {
   startAppServer,
   stopAppServerAndExit,
 } from "@ethicdevs/react-monolith";
+import fastifyGitServer, { GitServer } from "@ethicdevs/fastify-git-server";
 // 3rd-party
 import fastifyCookie, { CookieSerializeOptions } from "@fastify/cookie";
 import fastifyServeStatic from "fastify-static";

...
@@ -19,6 +20,7 @@ import {
   localAppDomainPreHandler,
   makeRequestHandler,
 } from "./utils/server";
+import { join, resolve } from "node:path";
 
 const HOST = process.env.HOST || "localhost";
 const PORT = process.env.PORT || 4100;

...
@@ -107,16 +109,37 @@ async function main(): Promise<AppServer> {
         parseOptions: cookiesOpts,
       });
 
+      s.register(fastifyGitServer, {
+        async authorize(repoSlug, { username, password }) {
+          const [org, repo] = repoSlug.split("/");
+          return (
+            org === "wnemencha" &&
+            repo === "react-monolith-samples" &&
+            username === "wnemencha" &&
+            password === "secret"
+          );
+        },
+        async repositoryResolver(repoSlug) {
+          const [org, repo] = repoSlug.split("/");
+          return {
+            authMode: GitServer.AuthMode.ALWAYS,
+            gitRepositoryDir: resolve(
+              join(Env.GIT_REPOSITORIES_ROOT, org, repo)
+            ),
+          };
+        },
+      });
+
       s.decorateReply("makeRequestHandler", makeRequestHandler);
-    },
-  });
 
-  server.get("/interceptor-imsw.js", {}, async (_, reply) => {
-    return reply.sendFile("interceptor-imsw.js");
-  });
+      s.get("/interceptor-imsw.js", {}, async (_, reply) => {
+        return reply.sendFile("interceptor-imsw.js");
+      });
 
-  server.get("/register-imsw.js", {}, async (_, reply) => {
-    return reply.sendFile("register-imsw.js");
+      s.get("/register-imsw.js", {}, async (_, reply) => {
+        return reply.sendFile("register-imsw.js");
+      });
+    },
   });
 
   await startAppServer(server);

app/services/auth/compareUserPasswordHashes.ts
@@ -1,8 +1,7 @@
 import type { AuthServiceDeps } from "./types";
 
-export function makeCompareUserPasswordHashes(deps: AuthServiceDeps) {
+export function makeCompareUserPasswordHashes(_: AuthServiceDeps) {
   const compareUserPasswordHashes = (a: string, b: string) => {
-    deps.request.id;
     return a === b;
   };
 

app/services/auth/index.ts
@@ -1,17 +1,16 @@
+// 1st-party
+import { makeService } from "@ethicdevs/react-monolith";
+// app
 import type { AuthServiceAPI, AuthServiceDeps } from "./types";
-
 import { makeCompareUserPasswordHashes } from "./compareUserPasswordHashes";
 import { makeIsExistingUsername } from "./makeIsExistingUsername";
 
-export const makeAuthService: ServiceFactory<AuthServiceAPI, AuthServiceDeps> =
-  (dependencies) => {
-    return withDependencies<AuthServiceAPI, AuthServiceDeps>(dependencies, {
-      compareUserPasswordHashes: makeCompareUserPasswordHashes,
-      isExistingUsername: makeIsExistingUsername,
-      isExistingUserUid: () => () => undefined,
-      findUserByUid: () => () => undefined,
-      findUserByUsername: () => () => undefined,
-      sendUserEmailAddressV8nEmail: () => () => undefined,
-      validateUserEmailAddress: () => () => undefined,
-    });
-  };
+export const makeAuthService = makeService<AuthServiceAPI, AuthServiceDeps>({
+  compareUserPasswordHashes: makeCompareUserPasswordHashes,
+  isExistingUsername: makeIsExistingUsername,
+  isExistingUserUid: () => () => undefined,
+  findUserByUid: () => () => undefined,
+  findUserByUsername: () => () => undefined,
+  sendUserEmailAddressV8nEmail: () => () => undefined,
+  validateUserEmailAddress: () => () => Promise.resolve(undefined),
+});

app/services/auth/types.ts
@@ -1,13 +1,25 @@
+import { FastifyRequest } from "fastify";
+import {
+  ServiceApiContract,
+  ServiceDependencies,
+} from "@ethicdevs/react-monolith";
+
 export interface AuthServiceAPI extends ServiceApiContract {
-  compareUserPasswordHashes: (a: string, b: string) => boolean;
-  isExistingUsername: (username: string) => boolean;
-  isExistingUserUid: (userUid: string) => boolean;
-  findUserByUsername: (username: string) => void;
-  findUserByUid: (userUid: string) => void;
-  sendUserEmailAddressV8nEmail: (args: unknown[]) => void;
-  validateUserEmailAddress: (userUid: string, emailAddress: string) => void;
+  compareUserPasswordHashes(a: string, b: string): boolean;
+  isExistingUsername(username: string): boolean;
+  isExistingUserUid(userUid: string): boolean;
+  findUserByUsername(username: string): void;
+  findUserByUid(userUid: string): void;
+  sendUserEmailAddressV8nEmail(args: unknown[]): void;
+  validateUserEmailAddress(
+    userUid: string,
+    emailAddress: string
+  ): Promise<void>;
 }
 
 export interface AuthServiceDeps extends ServiceDependencies {
+  // i.e. to access request decorators, one may pass request as a dependency
+  //      to this service and all the methods within it would have access to
+  //      a reference to it (i.e; database, cache, another service, etc...)
   request: FastifyRequest;
 }

file deleted
app/types.services.ts
@@ -1,53 +0,0 @@
-// TODO: extract the services things into its own package
-
-type ServiceApiContract = Record<string, ServiceMethod>;
-type ServiceDependencies = Record<string, any>;
-
-type ServiceMethod<
-  Args extends any[] = any[],
-  Return extends unknown = unknown
-> = (...args: Args) => Return;
-
-interface ServiceMethodFactory<
-  Deps extends ServiceDependencies = ServiceDependencies,
-  Args extends any[] = any[],
-  Return extends unknown = unknown
-> {
-  (deps: Deps): ServiceMethod<Args, Return>;
-}
-
-type Service<ApiContract extends ServiceApiContract = ServiceApiContract> =
-  ApiContract;
-
-interface ServiceFactory<
-  ApiContract extends ServiceApiContract,
-  ServiceDeps extends ServiceDependencies = ServiceDependencies
-> {
-  (deps: ServiceDeps): Service<ApiContract>;
-}
-
-function withDependencies<
-  ApiContract extends ServiceApiContract,
-  Deps extends ServiceDependencies = ServiceDependencies,
-  InjectableMethods extends Record<string, ServiceMethodFactory<Deps>> = Record<
-    string,
-    ServiceMethodFactory<Deps>
-  >
-  //InjectedMethods extends {
-  //  [K in keyof InjectableMethods]: ReturnType<InjectableMethods[K]>;
-  //} = { [K in keyof InjectableMethods]: ReturnType<InjectableMethods[K]> }
->(
-  dependencies: Deps,
-  injectableMethods: InjectableMethods
-): Service<ApiContract> {
-  const methodsEntries = Object.entries(injectableMethods);
-  const service = methodsEntries.reduce((apiContractAcc, [name, factoryFn]) => {
-    apiContractAcc = {
-      ...apiContractAcc,
-      [name]: factoryFn(dependencies),
-    };
-    return apiContractAcc;
-  }, {} as Service<ApiContract>);
-
-  return service;
-}

app/utils/style/NamedColors.ts
@@ -1,6 +1,10 @@
 import { default as Colors } from "./Colors";
 
 const NamedColors = {
+  BACKGROUND: {
+    light: Colors.GRAY_LIGHT_05,
+    dark: Colors.BLACK_01,
+  },
   BORDER_DEFAULT: {
     dark: Colors.GRAY_DARK_02,
     light: Colors.GRAY_LIGHT_02,

...
@@ -9,26 +13,10 @@ const NamedColors = {
     dark: Colors.GRAY_LIGHT_04,
     light: Colors.GRAY_LIGHT_02,
   },
-  TEXT_DEFAULT: {
-    light: Colors.BLACK_01,
-    dark: Colors.WHITE_01,
-  },
-  TEXT_MUTED: {
-    light: Colors.GRAY_LIGHT_06,
-    dark: Colors.GRAY_DARK_06,
-  },
-  TEXT_LINK: {
-    light: Colors.CYAN_01,
-    dark: Colors.CYAN_01,
-  },
   BRAND_LINE: {
     light: Colors.PRIMARY_01,
     dark: Colors.PRIMARY_01,
   },
-  BACKGROUND: {
-    light: Colors.GRAY_LIGHT_01,
-    dark: Colors.GRAY_DARK_01,
-  },
   CARD: {
     dark: Colors.GRAY_DARK_02,
     light: Colors.WHITE_01,

...
@@ -39,7 +27,7 @@ const NamedColors = {
   },
   HEADER: {
     light: Colors.WHITE_01,
-    dark: Colors.BLACK_01,
+    dark: Colors.GRAY_DARK_05,
   },
   SIDE_MENU: {
     light: Colors.WHITE_01,

...
@@ -57,6 +45,18 @@ const NamedColors = {
     light: Colors.GRAY_LIGHT_03,
     dark: Colors.GRAY_DARK_04,
   },
+  TEXT_DEFAULT: {
+    light: Colors.BLACK_01,
+    dark: Colors.WHITE_01,
+  },
+  TEXT_MUTED: {
+    light: Colors.GRAY_LIGHT_06,
+    dark: Colors.GRAY_DARK_06,
+  },
+  TEXT_LINK: {
+    light: Colors.CYAN_01,
+    dark: Colors.CYAN_01,
+  },
 };
 
 export default NamedColors;

app/views/DocHomeView.tsx
@@ -5,7 +5,7 @@ import styled from "styled-components";
 
 // app
 import type { CommonProps } from "../types";
-import { Layout, DocSectionEntryCard } from "../components";
+import { Layout } from "../components";
 
 export interface DocHomeViewProps extends CommonProps {}
 

...
@@ -14,25 +14,7 @@ const DocHomeView: ReactView<DocHomeViewProps> = (props) => {
   return (
     <Layout {...commonProps} showSideMenu={true}>
       <StyledDocHomeWrapper>
-        <StyledDocSectionEntryCardsGrid>
-          {commonProps?.menuDefinition != null &&
-            Object.entries(commonProps.menuDefinition).map(
-              ([sectionSlug, section]) => (
-                <DocSectionEntryCard
-                  key={sectionSlug}
-                  themeScheme={commonProps?.themeScheme}
-                  href={`/docs/${sectionSlug}/${
-                    Object.keys(section.pagesBySlug)[0]
-                  }`}
-                  coverImageUrl={
-                    "/public/assets/images/the-big-picture_light-on-dark.svg"
-                  }
-                  title={section.title}
-                  summary={section.summary}
-                />
-              )
-            )}
-        </StyledDocSectionEntryCardsGrid>
+        <StyledDocSectionEntryCardsGrid></StyledDocSectionEntryCardsGrid>
       </StyledDocHomeWrapper>
     </Layout>
   );

app/views/HomeView.tsx
@@ -5,8 +5,7 @@ import styled from "styled-components";
 
 // app
 import type { CommonProps } from "../types";
-import { Layout, DocSectionEntryCard, ButtonAnchor } from "../components";
-import { ReactMonolithLogo } from "../components/icons";
+import { ButtonAnchor, Layout } from "../components";
 
 export interface HomeViewProps extends CommonProps {
   foo?: boolean;

...
@@ -17,29 +16,10 @@ const HomeView: ReactView<HomeViewProps> = (props) => {
   return (
     <Layout {...commonProps} showSideMenu={false}>
       <StyledHomeWrapper>
-        <ReactMonolithLogo size={256} />
         <StyledButtonsRow>
-          <ButtonAnchor href={"/docs"}>Get Started</ButtonAnchor>
-          <ButtonAnchor href={"/api-reference"}>API Reference</ButtonAnchor>
+          <ButtonAnchor href={"/auth/register"}>Get Started</ButtonAnchor>
+          <ButtonAnchor href={"/features"}>Learn More</ButtonAnchor>
         </StyledButtonsRow>
-        <StyledCardsGrid>
-          {commonProps?.menuDefinition != null &&
-            Object.entries(commonProps.menuDefinition)
-              .slice(0, 3)
-              .map(([sectionSlug, section]) => (
-                <DocSectionEntryCard
-                  key={sectionSlug}
-                  themeScheme={commonProps?.themeScheme}
-                  href={`/docs/${sectionSlug}/${
-                    Object.keys(section.pagesBySlug)[0]
-                  }`}
-                  coverImageUrl={""}
-                  title={section.title}
-                  summary={section.summary}
-                />
-              ))}
-        </StyledCardsGrid>
-        <ButtonAnchor href={"/docs"}>Discover More</ButtonAnchor>
       </StyledHomeWrapper>
     </Layout>
   );

...
@@ -58,15 +38,6 @@ const StyledHomeWrapper = styled.div`
   margin: 0 auto;
 `;
 
-const StyledCardsGrid = styled.div`
-  display: flex;
-  flex-flow: row wrap;
-  align-items: stretch;
-  justify-content: center;
-  gap: 16px;
-  margin-top: 24px;
-`;
-
 const StyledButtonsRow = styled.div`
   display: flex;
   flex-flow: row wrap;

@@ -16,7 +16,8 @@
   },
   "dependencies": {
     "@ethicdevs/fastify-stream-react-views": "1.9.9",
-    "@ethicdevs/react-monolith": "1.5.0",
+    "@ethicdevs/fastify-git-server": "1.0.0",
+    "@ethicdevs/react-monolith": "1.6.1",
     "@fastify/cookie": "6.0.0",
     "cross-fetch": "^3.1.5",
     "dotenv-flow": "^3.2.0",

@@ -291,6 +291,13 @@
   version "0.7.5"
   resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
 
+"@ethicdevs/fastify-git-server@1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@ethicdevs/fastify-git-server/-/fastify-git-server-1.0.0.tgz#741b5873b799395a7bca4fba684b291c84ae1ab2"
+  dependencies:
+    debug "^4.3.4"
+    fastify-plugin "^3.0.1"
+
 "@ethicdevs/fastify-stream-react-views@1.9.9":
   version "1.9.9"
   resolved "https://registry.yarnpkg.com/@ethicdevs/fastify-stream-react-views/-/fastify-stream-react-views-1.9.9.tgz#3b1bd2da1170252eac926ba244fd248597c2a738"

...
@@ -308,9 +315,9 @@
     terser "^5.14.1"
     transform-modules-eumd "^1.0.0"
 
-"@ethicdevs/react-monolith@1.5.0":
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/@ethicdevs/react-monolith/-/react-monolith-1.5.0.tgz#419326efef717b93dea19d608aa645ec73d4573a"
+"@ethicdevs/react-monolith@1.6.1":
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/@ethicdevs/react-monolith/-/react-monolith-1.6.1.tgz#5b347344bef5545a1ac2b07dff74330321f9f272"
   dependencies:
     deepest-merge "^1.0.0"
     react-ssr-prepass "^1.5.0"

...
@@ -1217,7 +1224,7 @@ debug@2.6.9:
   dependencies:
     ms "2.0.0"
 
-debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1:
+debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.4:
   version "4.3.4"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
   dependencies: