diff --git a/.vscode/settings.json b/.vscode/settings.json index e2e1b58..bea748a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,18 @@ "typescript.tsdk": "frontend/node_modules/typescript/lib", "eslint.format.enable": true, "eslint.codeActionsOnSave.mode": "all", + "[javascript]": { + "editor.formatOnSave": false, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + } + }, + "[javascriptreact]": { + "editor.formatOnSave": false, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + } + }, "[typescript]": { "editor.formatOnSave": false, "editor.codeActionsOnSave": { diff --git a/assets/client-server-interaction.drawio b/assets/client-server-interaction.drawio index 75995a7..cb756fc 100644 --- a/assets/client-server-interaction.drawio +++ b/assets/client-server-interaction.drawio @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/assets/client-server-interaction.svg b/assets/client-server-interaction.svg index b308b42..0784156 100644 --- a/assets/client-server-interaction.svg +++ b/assets/client-server-interaction.svg @@ -1,3 +1,3 @@ -
Links editor page
Links editor page
caffeine
caffeine
Response /srv/www-csc-links/index.html
Response /srv/www-csc-links/index.html
GET /
GET /
Links page
Links page
GET /links.json
GET /links.json
Response /srv/www-csc-links/links.json (does not include click count)
Response /srv/www-csc-links/links.json (does not include click co...
Consider / to be https://csclub.uwaterloo.ca/links
Consider / to be https://csclub.uwaterloo.ca/...
ONLY when a link is clicked:
POST /api/click with { "url": <url>, "name": <name> } as payload
ONLY when a link is clicked:...
200 OK
200 OK
Backend
(python)
Backend...
GET /editor/links (authenticated)
GET /editor/links (authenticated)
Response /srv/www-csc-links/editor/index.html
Response /srv/www-csc-links/editor/index.html
Dynamically generated links with click count
Dynamically generated links with click count
GET /editor (authenticated)
GET /editor (authenticated)
sqlite
sqlite
POST /editor/links (authenticated) with updated JSON recvd from /editor/links
POST /editor/links (authenticated) with updated JSON recvd from /editor...
200 OK
200 OK
Viewer does not support full SVG 1.1
\ No newline at end of file +
Links editor page
Links editor page
caffeine
caffeine
Response /srv/www-csc-links/index.html
Response /srv/www-csc-links/index.html
GET /
GET /
Links page
Links page
GET /links.json
GET /links.json
Response /srv/www-csc-links/links.json (does not include click count)
Response /srv/www-csc-links/links.json (does not include click co...
Consider / to be https://csclub.uwaterloo.ca/links
Consider / to be https://csclub.uwaterloo.ca/...
ONLY when a link is clicked:
POST /api/click with { "url": <url>, "name": <name> } as payload
ONLY when a link is clicked:...
200 OK
200 OK
Backend
(python)
Backend...
GET /api/editor/links (authenticated)
GET /api/editor/links (authenticat...
Response /srv/www-csc-links/editor/index.html
Response /srv/www-csc-links/editor/index.html
Dynamically generated links with click count
Dynamically generated links with click count
GET /editor (authenticated)
GET /editor (authenticated)
sqlite
sqlite
POST /api/editor/links (authenticated) with updated JSON recvd from /api/editor/links
POST /api/editor/links (authenticated) with updated JSON recvd from /ap...
200 OK
200 OK
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/backend/main.py b/backend/main.py index 49e23da..3ceebe7 100644 --- a/backend/main.py +++ b/backend/main.py @@ -8,7 +8,7 @@ DB_PATH = os.path.join(os.path.dirname(__file__), 'links.db') @app.route('/') def hello_world(): - return 'Hello, World!' + return 'Hello from backend!' def regen_JSON(): """Gets links from DB and outputs them in JSON""" diff --git a/frontend/next.config.js b/frontend/next.config.js new file mode 100644 index 0000000..b5619f4 --- /dev/null +++ b/frontend/next.config.js @@ -0,0 +1,29 @@ +// @ts-check + +/* eslint-disable @typescript-eslint/no-var-requires */ + +// eslint-disable-next-line no-undef +const { PHASE_DEVELOPMENT_SERVER } = require("next/constants"); + +const devConfig = { + async rewrites() { + return [ + { + source: "/api", + destination: "http://localhost:5000", + }, + { + source: "/api/:slug", + destination: "http://localhost:5000/:slug", + }, + ]; + }, +}; + +const prodConfig = { + basePath: "/links", +}; + +// eslint-disable-next-line no-undef +module.exports = (phase) => + phase === PHASE_DEVELOPMENT_SERVER ? devConfig : prodConfig; diff --git a/frontend/pages/hello-world.json b/frontend/pages/hello-world.json new file mode 100644 index 0000000..c4040a6 --- /dev/null +++ b/frontend/pages/hello-world.json @@ -0,0 +1,3 @@ +{ + "greeting": "Hello from JSON" +} \ No newline at end of file diff --git a/frontend/pages/hello-world.tsx b/frontend/pages/hello-world.tsx new file mode 100644 index 0000000..a21debe --- /dev/null +++ b/frontend/pages/hello-world.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { GetStaticProps } from "next"; +import GreetingJSON from "./hello-world.json"; +import { fetchExample } from "utils/api"; + +interface Props { + greeting: string; +} + +export const getStaticProps: GetStaticProps = async () => { + return { + props: { greeting: GreetingJSON.greeting }, // will be passed to the page component as props + // Next.js will attempt to re-generate the page: + // - When a request comes in + // - At most once every second + revalidate: 1, + }; +}; + +function HelloWorld({ greeting }: Props): JSX.Element { + const [greetings, setGreetings] = React.useState([greeting]); + + const getMoreGreetings = async () => { + const greetingFromBackend = await fetchExample(); + setGreetings([ + ...greetings, + `"${greetingFromBackend}" --- ${new Date().toISOString()}`, + ]); + }; + + return ( +
+ +
    + {greetings.map((greeting) => ( +
  1. {greeting}
  2. + ))} +
+
+ ); +} + +export default HelloWorld; diff --git a/frontend/utils/api.ts b/frontend/utils/api.ts new file mode 100644 index 0000000..771e2ac --- /dev/null +++ b/frontend/utils/api.ts @@ -0,0 +1,4 @@ +export async function fetchExample(): Promise { + const response = await fetch("/api"); + return await response.text(); +}