├── EPICS ├── example_readme.md ├── policies.md ├── review.md ├── student_onboard.md ├── tech_stack.md └── vscode_extensions.md ├── README.md ├── architecture_and_systems ├── authentication.md ├── component_based_design.md ├── data_flow.md ├── databases.md ├── frontend_vs_backend_routing.md ├── state_management.md └── users_roles_and_permissions.md ├── demos ├── html_css_js │ ├── README.md │ └── index.html ├── react-demo │ └── package.json ├── servers │ ├── .env │ ├── .gitignore │ ├── README.md │ ├── db.db │ ├── index.js │ ├── package.json │ ├── pnpm-lock.yaml │ ├── prisma │ │ ├── db.db │ │ ├── migrations │ │ │ ├── 20240908145758_initial │ │ │ │ └── migration.sql │ │ │ └── migration_lock.toml │ │ └── schema.prisma │ └── public │ │ └── index.html └── vue-demo │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierrc.json │ ├── .vscode │ └── extensions.json │ ├── README.md │ ├── env.d.ts │ ├── index.html │ ├── package.json │ ├── pnpm-lock.yaml │ ├── public │ └── favicon.ico │ ├── src │ ├── App.vue │ ├── assets │ │ └── logo.svg │ ├── components │ │ ├── TodoItem.vue │ │ ├── __tests__ │ │ │ └── HelloWorld.spec.ts │ │ └── icons │ │ │ ├── IconCommunity.vue │ │ │ ├── IconDocumentation.vue │ │ │ ├── IconEcosystem.vue │ │ │ ├── IconSupport.vue │ │ │ └── IconTooling.vue │ ├── main.ts │ ├── router │ │ └── index.ts │ ├── stores │ │ └── todos.ts │ └── views │ │ ├── Todo.vue │ │ └── Todos.vue │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── tsconfig.vitest.json │ ├── vite.config.ts │ └── vitest.config.ts ├── end_of_sem.md ├── languages_and_frameworks ├── express.md └── webdev_pathway.md ├── npts_links ├── tooling ├── docker_wsl_setup.md ├── documentation_tools.md ├── files.md ├── git_tutorials.md ├── node_nvm_npm_pnpm.md ├── prisma_edge_functions_setup.md └── repo_cli_setup.md └── webdev_playlist.md /EPICS/example_readme.md: -------------------------------------------------------------------------------- 1 | # Example README.md 2 | 3 | ## Conceptual Overview 4 | 5 | This system is focused around advocates, families, and pages. Advocates invite families and guide them through the process of creating a memorial page. These pages contain obituaries, times, and locations. These pages are publicly viewable and searchable. Pages can receive donations, have donation goals, and have deadlines for fundraising. Family users can only access the system after being invited by an advocate. Family users have profiles. 6 | 7 | ## Functional Requirements 8 | 9 | ### Users 10 | 11 | - There are two roles: family and advocate 12 | - Users are invite only, regardless of roles 13 | - Only advocates can 14 | - Invite users 15 | - See a lit of all pages 16 | - Change the role of a user 17 | - All users can 18 | - Edit a page 19 | - See a list of their own pages 20 | - Users are authenticated via Auth0 21 | - Users receive invitation emails via AWS SES 22 | 23 | ### Pages 24 | 25 | - All pages can be viewed publicly 26 | - Pages must be publicly searchable 27 | - Pages have 28 | - Name 29 | - Day of birth 30 | - Day of passing 31 | - Visitation 32 | - Date 33 | - Location 34 | - Description 35 | - Funeral 36 | - Date 37 | - Location 38 | - Description 39 | - Obituary 40 | - Donation goal 41 | - Amount donated 42 | - Deadline 43 | - Images 44 | - Donations 45 | - Processed through stripe 46 | 47 | ### Stack 48 | 49 | Nuxt, postgres, prisma 50 | 51 | ### Research Questions 52 | 53 | None 54 | 55 | ### Third party integrations 56 | 57 | - Stripe for payments 58 | - Auth0 for authentication 59 | - AWS SES for emails 60 | 61 | ### Deployment 62 | 63 | AWS EC2 64 | -------------------------------------------------------------------------------- /EPICS/policies.md: -------------------------------------------------------------------------------- 1 | # Policies 2 | 3 | These are general policies to follow when working on your projects. 4 | 5 | ## Git/GitHub 6 | 7 | - Branches: 8 | - All branches must be named with the syntax `dev//` 9 | - All branches must be merged and cleaned out by the final presentation 10 | - All [feature branches](https://www.optimizely.com/optimization-glossary/feature-branch/) must stay updated (merge in changes from other branches) 11 | - All tasks must have a GitHub issue created 12 | - All GitHub pull requests (PRs) must link to issues that they close 13 | - Do not create your own repositories/forks 14 | - All projects must use GitHub Projects for their project planning 15 | - Do NOT merge branches locally and then push them - push the branch and create a pull request 16 | - The exception here is your own local dev work. What you push up is for review by others before merging in. 17 | - Do NOT commit node_modules - make sure [.gitignore](https://git-scm.com/docs/gitignore) is properly configured. Most scaffolds should handle this automatically. 18 | - Repository authentication is either through logging in via [VSCode](https://code.visualstudio.com), [GitHub Desktop](https://desktop.github.com), or through [SSH](https://docs.github.com/en/authentication/connecting-to-github-with-ssh) 19 | 20 | ## Development Stack 21 | 22 | - Databases should be instantiated using a `docker-compose` file (unless the project is using SQLite) 23 | - [Prisma](https://www.prisma.io) is required for use with all new projects involving a database 24 | 25 | ## Other 26 | 27 | - Branches, PRs, and issues are reviewed as part of the mid semester and final presentations 28 | - Server authentication for deployments is done through SSH 29 | - SSH should be ED25519 (smaller, faster, and more secure than default) 30 | - Avoid the use of large UI frameworks. Use something lightweight and functionality oriented like Headless UI or just build the component yourself. 31 | - Students use LLMs at their own risk. They are ultimately responsible for the code they submit. If they can't verify the correctness of the code, they should be writing it themselves. Submitting output from an LLM that is incorrect is worse than incorrect code they wrote themselves. It is strongly recommended that students NOT attempt to use tools that they don't understand to generate code that they don't understand. 32 | - Students MUST NOT create their own repositories. 33 | - Students are responsible for learning how to use the tools. No one should be asking how to make a commit or push a branch at the end of the semester. 34 | - Branches should be specific to an issue. Students should not be copy/pasting code between each others branches because it can complicate PRs and cause conflicts if not done correctly. Students should properly track which tasks are dependencies of other tasks, plan their timelines and work appropriately, and merge work into their dev as needed. 35 | - Students should NOT be pushing up multiple versions of a branch with suffixes such as '-v2'. If a student has new work for a branch, they should just update that branch. If they have work for a different feature that depends on that branch, they should create a new branch off of the old branch with a new name and issue reference, and reference the old branch in the PR. 36 | -------------------------------------------------------------------------------- /EPICS/review.md: -------------------------------------------------------------------------------- 1 | # Proper use of relations when rendering data 2 | 3 | https://github.com/UTDallasEPICS/UTDesign-Procurement/blob/567957d137cc3bbf672a16fa783c9af6dfa1f9ff/procurement-manager/pages/orders/admin.tsx#L1 4 | 5 | In this example, we are loading a list of projects, and then for each project we are making another prisma query to load the requests. Then in the UI, we are relying on the index of a project in the projects array and the index of the corresponding list of requests being the same. This should be leveraging prisma relations to join the tables in one query so that each project has its requests as an array on itself. 6 | 7 | We also have two functions, fetchProjects and fetchVendors that do not do anything – they are called in the custom search function, but they do not perform any state updates. 8 | 9 | Because of how we need to fetch both initial data and data when searches are performed, the data requests should all be done using useEffect instead of getServerSideProps – this way there is less code to manage and we do not have to worry about juggling using data from props vs data that is fetched clientside. In general, getServerSideProps should _only_ be used for data that is _not_ expected to change in response to a user action. Supporting search for a table means that we will have to perform data fetching from the client side and cannot rely exclusively on server side rendering. 10 | 11 | We also need to be enforcing clean up of commented out code – that code needs either an explanation as to why it is commented out, or it needs to be removed entirely. 12 | 13 | # Long names and nested folder structures 14 | 15 | https://github.com/UTDallasEPICS/Legacy-Housekeeping-QC/tree/AWSIMAGEUPLOAD/src/components/roomScreenDashboard/componentsForFloorSelection 16 | 17 | Discourage overly nested structures and long file/folder names. The above example can replace 'componentsForFloorSelection' with simply "FloorSelection". There are also deeply nested folder structures that can be condensed, largely by creating generalized components. For example, there are several variations on doing a grid of cards - there should be one Card component, and one Grid component, and then they are composed to produce a page, instead of creating a different component file for every possible usage. 18 | 19 | # Secrets and URLs 20 | 21 | Need to be handled via .env. We should not be hard coding something like 22 | 23 | ```js 24 | const res = await fetch("https://127.0.0.1:5000/api/endpoint"); 25 | ``` 26 | 27 | as this is _not_ a portable way to define API endpoints. Instead, 28 | 29 | ```js 30 | const res = await fetch(import.meta.env.API_URL + "/api/endpoint"); 31 | ``` 32 | 33 | However, if we are making a request to our own API, we do not have specify a domain at all as our Nuxt/Next projects all run on the same domain. We can simply do 34 | 35 | ```js 36 | const res = await fetch("/api/endpoint"); 37 | ``` 38 | 39 | Secrets (even not-secret things like frontend API keys) must also be handled with .env so that we do not have to rewrite code to change which API keys are used. 40 | 41 | # Requirements Review 42 | 43 | - Create requirements document for project (Requirements.md) 44 | - Functional Requirements - what does it need to do? (What do users need to be able to do?) 45 | - Process Requirements - what does the user journey look like? (How does a user achieve the objective with the interface?) 46 | - Implementation Requirements - how do we implement that user journey? (API routes, buttons, forms, functions) 47 | - Establish and document which requirements have been fulfilled 48 | - Create an issue with unfulfilled requirements listed 49 | 50 | # Code Reviews 51 | 52 | ## Application Structure 53 | 54 | - Componentization of UI: using components to cut down on code duplication, with proper usage of props and events to send and receive information. 55 | - components with large component trees may need to use a pinia store to minimize prop drilling (where a prop is passed many times down through the component tree). 56 | 57 | ### common patterns 58 | 59 | - components with duplicate functionality should try to use composables (not expected to be very common) 60 | - Components are used to unify styling of semantic elements, such as labels and buttons. 61 | - Putting universal components everywhere instead of using the top level App component, e.g. navbars, notifications, etc are copied to every page instead of only existing once in the top level App or layout component. 62 | - API routes are named with proper semantics and formatting (e.g. user.get.ts (nuxt) or user.ts (next) instead of getUser.get.ts, users.get.ts for bulk operations) 63 | - database clients should either be implemented in middleware (`event.context.client` in Nuxt) or as an export from a `db.ts` file and NOT imported & instantiated in every route handler because otherwise we end up with a ton of database connections. 64 | - Do not use the native date input as it does not nicely handle timezones in its output. Use a datepicker library that outputs an ISO string in UTC. Complicated browser controls are also often difficult to style nicely, with pseudoelements that are not the same across all browsers. 65 | 66 | ## Code Flow & Quality 67 | 68 | - proper usage of framework primitives (e.g. refs, computeds, watchers for Vue, useEffect and useState for React) 69 | - in particular, students tend to under-utilize computeds and watchers, instead writing functions that directly set a value when called or have to be explicitly called instead of automatically reacting to changes 70 | 71 | ```jsx 72 | let f = 0 73 | const incomingData = {total:6} 74 | function setF(incomingData) { 75 | f = incomingData.total 76 | } 77 | setF(incomingData) 78 | // vs 79 | const incomingData = ref({total:6}) 80 | const f = computed(() => incomingData.value.total) 81 | ``` 82 | 83 | - Students sometimes take several loops to achieve something that can be done in one 84 | - Students under-utilize iterator functions such as `for of`, `filter`, `map`, `reduce`, `Object.entries` 85 | - Students tend to leave lots of commented out old code all over the place. students must get used to traversing the commit history to recover old code 86 | - Students tend to leave `console.log` all over the place. these must either be gated behind a debug flag or removed when no longer needed for testing 87 | - Students tend to have terrible formatting. all projects must have linting setup. students must have lint-on-save setup and we should have pre-commit hooks for every repo that run the linter. 88 | - Students under-utilize `async/await`, instead using the `.then` pattern. 89 | 90 | ## Database 91 | 92 | - all projects must use Prisma as their ORM 93 | - names of properties on Prisma models should *not* include the model name. I already know that `User.first_name` is on the User model, `User.user_first_name` is redundant. 94 | - Prisma model definitions must use the following syntax 95 | ```prisma 96 | model User { 97 | id Int @id 98 | snake_case String 99 | relationId Int @map("relation_id") 100 | Relation Relation @relation(fields: [relation_id], references: [id]) 101 | @@map("users") 102 | } 103 | model Relation { 104 | id Int @id @default(autoincrement()) 105 | } 106 | ``` 107 | 108 | ## Dependencies 109 | 110 | - students sometimes add many extraneous dependencies. check for any that are unused. 111 | - All projects must use tailwind (we still need to teach CSS though) 112 | - all projects must use Auth0, or SSO for internal UTD projects 113 | - we strongly discourage UI libraries. Headless UI provides sufficient functionality for most use cases while allowing full customization of the UI, and an important part of the learning experience is understanding how to build UI components. It is not that difficult to create a Card or Table component, and the UX/UI designers will always end up asking for something custom anyways so it's important to know how to build them yourself. I am interested in any additional libraries like Headless that allow for the functionality of a UI element to be combined with an arbitrary UI, as while UX/UI designers often want creative and different UI, they rarely want truly novel functionality. This makes functionality oriented libraries like Headless more flexible and broadly applicable. Headless and Tailwind are also usable across multiple frameworks with first-class support, as opposed to libraries such as Shadcn which is React only. 114 | 115 | ## Test data 116 | 117 | - seed.ts 118 | - should take user email address via .env so that students can pass their email address in without having to commit it 119 | - must create test data for all tables 120 | 121 | ## Authorization 122 | 123 | - check that route access is properly gated based on user role in the backend. Frontend can rely on conditional rendering to simply hide relevant UI elements, but the backend must properly reject unauthorized actions 124 | - loading the user profile into the frontend can be accomplished with either cookies or by creating an API route that the frontend hits on load. 125 | - we do not store role information in Auth0, we only use them as a login provider 126 | 127 | ## Reference implementations 128 | 129 | ### Authentication with Auth0 130 | 131 | TODO 132 | 133 | ### File Uploads 134 | 135 | TODO 136 | 137 | # Team workflows during the semester 138 | 139 | - Students should _not_ be breaking up into frontend vs backend teams. Students should be responsible for features end to end. 140 | - Mentors can provide task guidance for the first few weeks, but eventually the students should be determining their own tasks based on their understanding of the project 141 | -------------------------------------------------------------------------------- /EPICS/student_onboard.md: -------------------------------------------------------------------------------- 1 | # Initial Student Onboarding (2 weeks) 2 | 3 | - GitHub Survey 4 | - Repository cloned 5 | - Skills Overview Survey 6 | - Dev environment setup 7 | 8 | ## Project Planning & Initial Design (3 weeks) 9 | 10 | ### New Projects 11 | 12 | #### Requirements Collection 13 | 14 | - Conceptual Overview (Readme.md) 15 | - Partner Docs Acquisition 16 | 17 | #### Project Planning 18 | 19 | - Initial Prisma Schema 20 | - Initial Figma UI (link in Readme.md) 21 | - Semester Project Plan (issues, GH Project) 22 | - Start coding 23 | 24 | ### Existing Project 25 | 26 | #### Requirements Collection 27 | 28 | - Review Docs 29 | - Review System 30 | 31 | #### Project Planning 32 | 33 | - Semester Project Plan (issues, GH Project) 34 | - Start coding 35 | 36 | ## Project Wrapup (last 2 weeks) 37 | 38 | - finish current tasks 39 | - update issues and readme 40 | - end of semester checklist 41 | -------------------------------------------------------------------------------- /EPICS/tech_stack.md: -------------------------------------------------------------------------------- 1 | # Tech Stack Options 2 | 3 | ## Web Projects 4 | 5 | ### Full-Stack Applications 6 | 7 | Full-Stack applications consist of a frontend, backend, and database. This is what most EPICS projects end up being. We use JavaScript metaframeworks, which integrate backend and frontend functionality into one codebase. 8 | 9 | [Nuxt](https://nuxt.com) (based on Vue.js) or [Next.js](https://nextjs.org) (based on React) are required for new full-stack projects. **Do not** create a standalone server (e.g. H3 or Express.js) without talking to your mentor first. 10 | 11 | 12 | ### Back-End Only Projects 13 | 14 | In some cases, we may need only a backend server. These are generally projects where you are creating an API or performing data processing. [Node](https://nodejs.org) with [H3](https://github.com/unjs/h3) is highly recommended. 15 | 16 | ### Single-Page Applications (SPA) 17 | 18 | If we only need a single-page application (SPA) [Vue.js](https://vuejs.org) or [React](https://react.dev) are recommended. There are many front-end frameworks, but these are by far the most popular (for good reason). 19 | 20 | ### Databases 21 | 22 | For web project databases, you may should choose an SQL database ([MySQL](https://www.mysql.com), [PostgreSQL](https://www.postgresql.org), or [SQLite](https://www.sqlite.org/index.html)). Though [MongoDB](https://www.mongodb.com) is popular, it is not optimal for most EPICS projects due to unstructured nature of the database. If you believe MongoDB is a good fit for your project, speak with your mentor. Either way, [Prisma](https://www.prisma.io) is highly recommended for database schema management. Prisma makes it much easier to define and understand how your database is set up and is compatible with all major databases. 23 | 24 | ### Environment 25 | 26 | We use [docker](https://www.docker.com) containers for running infrastructure locally (databases at a minimum, excluding SQLite). Students using Windows as their personal machine will need to setup [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install). 27 | 28 | ### Deployment 29 | 30 | We use [Amazon Web Services (AWS)](https://aws.amazon.com) for our cloud infrastructure, unless there are partner specific requirements. 31 | 32 | ## Non-Web Projects 33 | 34 | For non-web projects, tech stack is likely to be defined by the partner needs - we don't have many of these in the first place, so we don't have a good standard system yet. In the past, Python has been used for local GUIs that can also connect to hardware. 35 | -------------------------------------------------------------------------------- /EPICS/vscode_extensions.md: -------------------------------------------------------------------------------- 1 | # VSCode Extensions 2 | 3 | - [Color Highlight by naumovs](https://open-vsx.org/vscode/item?itemName=naumovs.color-highlight) 4 | 5 | - [Docker](https://open-vsx.org/extension/ms-azuretools/vscode-docker) 6 | - [Prisma](https://open-vsx.org/extension/Prisma/prisma) 7 | - [tailwindcss](https://open-vsx.org/extension/bradlc/vscode-tailwindcss) 8 | - [Vue Language Features (Volar)](https://open-vsx.org/extension/Vue/volar) 9 | - [Prettier - Code Formatter](https://open-vsx.org/extension/esbenp/prettier-vscode) 10 | - [HTML CSS Support](https://open-vsx.org/extension/ecmel/vscode-html-css) 11 | - [GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) 12 | - [Thunder Client](https://marketplace.visualstudio.com/items?itemName=rangav.vscode-thunder-client) 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Guides 2 | 3 | In here you will find various guides and notes. All of these have been prepared by current and previous EPICS students, mentors, and staff members. 4 | 5 | ## How to Use These Guides 6 | 7 | Each of these guides covers a different aspect of a typical web application developed in EPICS. For all students, we recommend starting with Taz's videos (link below). From there, many other guides are provided which will be useful as you progress through the semester. 8 | 9 | ### [Taz's Getting Started Videos](https://utdallas.box.com/v/epics-cs-introduction-videos) 10 | 11 | [https://utdallas.box.com/v/epics-cs-introduction-videos](https://utdallas.box.com/v/epics-cs-introduction-videos) 12 | 13 | We recommend all students working on CS projects watch these videos. Additionally, **watching all of these videos counts as one of your two required workshops for the semester**. 14 | These videos cover the following 5 topics: 15 | 16 | - Git (a command line tool for code management) 17 | - GitHub (a web application built around Git) 18 | - Bash (the command line) 19 | - Clients, servers, and databases 20 | - Browser Developer Tools 21 | 22 | ### Other Documents 23 | 24 | These documents go into more specific detail about various processes, tools, and frameworks that will be useful for web app development. 25 | 26 | #### EPICS Specific Stuff 27 | 28 | - [Tech Stack Recommendations](./EPICS/tech_stack.md) 29 | - [End of Semester Checklist](./EPICS/checklist.md) 30 | - [Example README.md](./EPICS/example_readme.md) 31 | - [General Policies and Recommendations](./EPICS/policies.md) 32 | - [Recommended VS Code Extensions](./EPICS/vscode_extensions.md) 33 | - [Onboarding Timeline](./EPICS/student_onboard.md) 34 | 35 | > WARNING: The following guides are NOT exhaustive. There is a significant amount of nuance that cannot be fit into this guide.They are not a replacement for reading the documentation and personal experimentation. Read the docs, experiment with writing code. The best way to learn is by doing. 36 | 37 | #### Language & Framework Info 38 | 39 | - [Express.js](./languages_and_frameworks/express.md) 40 | - [Web Development Pathway](./languages_and_frameworks/webdev_pathway.md) 41 | 42 | #### Tooling 43 | 44 | - [Node.js](./tooling/node_nvm_npm_pnpm.md) 45 | - [Docker and Windows Subsystem for Linux](./tooling/docker_wsl_setup.md) 46 | - [Documentation Tools](./tooling/documentation_tools.md) 47 | - [Node.js Files Explained](./tooling/files.md) 48 | - [Git Setup](./tooling/repo_cli_setup.md) 49 | - [Git Tutorials](./tooling/git_tutorials.md) 50 | 51 | #### Architecture & System Design 52 | 53 | - [Databases](./architecture_and_systems/databases.md) 54 | - [Data Flow in Applications](./architecture_and_systems/data_flow.md) 55 | - [Frontend vs Backend Routing](./architecture_and_systems/frontend_vs_backend_routing.md) 56 | - [Component Based Design](./architecture_and_systems/component_based_design.md) 57 | - [State Management](./architecture_and_systems/state_management.md) 58 | - [Users, Roles, and Permissions](./architecture_and_systems/users_roles_permissions.md) 59 | - [Authentication and Auth0](./architecture_and_systems/authentication.md) 60 | -------------------------------------------------------------------------------- /architecture_and_systems/authentication.md: -------------------------------------------------------------------------------- 1 | # Authentication and Auth0 2 | 3 | ## Safely Storing Passwords 4 | 5 | When a user registers or sets a password, it is crucial to store it securely. Instead of storing passwords directly, we use a process called hashing. Hashing converts a password into a fixed-length string of characters using a cryptographic algorithm. The resulting hash is unique to the input password, making it hard to reverse-engineer the original password. Not al hashing algorithms are equally secure - some algorithms are susceptible to what are called 'collisions', where two inputs can produce the same hash value. The longer the hash that is generated, the lower the probability of experiencing collisions - simple probability theory. However, hashing alone is not enough to protect against potential attacks like dictionary attacks or rainbow table attacks where a precomputed list of password hashes (the rainbow table) is compared to a stolen database. 6 | 7 | To enhance security, web applications also use salting. Salting involves adding a random string of characters (salt) to the password before hashing it. The salt is unique per user and stored alongside the hashed password. This makes it much harder for attackers to crack the passwords using rainbow tables, as the hashes in the rainbow table must be recalculated to account for the salt. 8 | 9 | ## Cookies 10 | 11 | Cookies are small pieces of data stored on a user's browser. They are commonly used for session management and authentication. When a user logs in, the server generates a unique session identifier and sends it to the client as a cookie. The client then includes this cookie in subsequent requests to the server. The server validates the session identifier to grant access to the requested resources. 12 | 13 | ## JSON Web Tokens (JWTs) 14 | 15 | JWTs are a popular method for authentication in web applications. They are self-contained, digitally signed tokens that contain user identity information. JWTs are encoded and consist of three parts: header, payload, and signature. The header specifies the hashing algorithm used, the payload includes user information, and the signature ensures the token's integrity. 16 | 17 | When a user logs in, the server generates a JWT and sends it to the client. The client includes the JWT in subsequent requests as an Authorization header or within a query parameter. The server then verifies the JWT's signature, ensuring its authenticity, and extracts the user information from the payload. 18 | 19 | ## Encryption 20 | 21 | Digital signing uses asymmetric encryption. This is a fascinating topic that underpins all of digital security, including digital identity, Web3/blockchain technology, and financial services. 22 | 23 | ### Public and Private Keypairs 24 | 25 | Public and private keypairs are generated together and are mathematically linked. They are used in asymmetric encryption to perform different operations. 26 | 27 | The fundamental property of asymmetric encryption is that data encrypted with a public key can only be decrypted with the corresponding private key, and vice versa. 28 | 29 | #### Public Key 30 | 31 | The public key is intended to be shared openly with anyone who wants to communicate securely with the key owner. It is used to encrypt data or verify digital signatures. Public keys are generally available to the public and can be freely distributed. 32 | 33 | As a side note, the 'address' used in blockchain technology is actually a public key. 34 | 35 | #### Private Key 36 | 37 | The private key, as the name suggests, is kept secret and known only to the key owner. It is used to decrypt data encrypted with the corresponding public key or to generate digital signatures. Private keys must be securely stored and should never be shared with anyone. Private keys may also themselves be encrypted and require a passphrase in order to use the actual key. This makes it so that an attacker must both know your passphrase and have access to the private key. 38 | 39 | As another side note, when you perform an operation on a blockchain you are using the private key that corresponds to the public key that is your wallet address. A 'hot' wallet is a private key that is stored on a machine so that it can be actively used for performing operations. A 'cold' wallet is one where the private key is stored offline, so that it cannot be compromised digitally and must be physically stolen. This is possible because it is the signed output that must be broadcast to the network - they already have the public key that they can use to verify the transaction. It is possible to generate a valid transaction doing the math by hand and typing in the right numbers. 40 | 41 | ### Encrypting Data 42 | 43 | In asymmetric encryption, the public key is used to encrypt data. When a sender wants to send a message to a specific recipient, they use the recipient's public key to encrypt the message. Once encrypted, only the recipient with the corresponding private key can decrypt and read the message. 44 | 45 | This process provides confidentiality and ensures that only the intended recipient can access the message. Even if the encrypted message is intercepted during transmission, it remains secure as only the private key holder can decrypt it. 46 | 47 | ### Signing Data 48 | 49 | Asymmetric encryption is also used for digital signatures. A digital signature provides integrity and authenticity to data, ensuring that it has not been tampered with and verifying the identity of the sender. 50 | 51 | To sign data, the sender uses their private key to generate a unique digital signature for the data. The recipient can then use the sender's public key to verify the signature. If the signature is valid, it confirms that the data was not modified during transmission and that it was indeed sent by the holder of the private key. 52 | 53 | ## Methods for Transmitting Authentication Info 54 | 55 | Authentication methods can vary based on how the authentication data is sent. The three common methods are: 56 | 57 | ### Query Parameter 58 | 59 | The authentication data is appended to the URL as a query parameter. For example: https://example.com/resource?token=123456789. 60 | 61 | ### Authorization Header (Bearer Token) 62 | 63 | The authentication data is sent in the Authorization header using the Bearer token scheme. For example: Authorization: Bearer 123456789. 64 | 65 | ### Cookies 66 | 67 | The authentication data is stored in cookies, as discussed earlier. The client automatically includes the cookie in subsequent requests, eliminating the need to explicitly send authentication data. Note that cookies cannot be accessed across domains. A cookie set by auth0.com cannot be read by utdallas.edu, for security reasons. One of the other two methods must be used for cross domain authentication, and this is where the next section comes in. 68 | 69 | ## Auth0 and OAuth 70 | 71 | All of the above is about how to implement an authentication system. At EPICS, we use Auth0 to handle the implementation of authentication. This also lets us easily use integrations such as sign in with a Google or Microsoft account via the OAuth protocol. Essentially, we rely on a third party to handle the storage and validation of authentication methods, and we only need to verify that a token we receive has been signed by our third party provider. 72 | 73 | ```mermaid 74 | sequenceDiagram 75 | participant Client 76 | participant OurServer 77 | participant Auth0 78 | 79 | Client->>OurServer: Request Page 80 | OurServer->>OurServer: Check Authentication Status 81 | OurServer->>OurServer: Authentication Not Found 82 | OurServer->>Auth0: Forward to Auth0 83 | Auth0->>Auth0: Handle Login 84 | Auth0->>OurServer: Forward User Back With Token 85 | OurServer->>Client: Redirect to Page 86 | ``` 87 | 88 | An important thing to note here is that the user is NOT logging in to us, they are instead logging into Auth0. Specifically, they are logging into our Auth0 Tenant, which is a specific instance that Auth0 hosts for us. Auth0 then sends us a token, which we verify using the public key for our Tenant. 89 | 90 | This is straightforward to implement ourselves, as we need only the following API routes (SPA handling is different): 91 | 92 | - login: redirects user to the auth0 login page 93 | - logout: redirects user to the auth0 logout page, which will automatically redirect them to.... 94 | - logout-complete: clear our cookies/auth storage 95 | This is with SSR, where we can conveniently store the token as a cookie and access it both server and client side. 96 | 97 | If we are doing an SPA, the client side library will handle all the redirects and we simply need to worry about sending the token or saving it to cookies. 98 | 99 | We also need some form of middleware function to handle verifying authenticity of the login token. In SSR, this middleware will be serverside, and will often run on every request. You can use this middleware to annotate incoming requests with information such as user profiles, roles, database clients, etc. SPAs can also guard the navigation routes clientside by checking authentication status, although this DOES NOT protect the API. 100 | 101 | An example of verifying a token: 102 | 103 | ```js 104 | 105 | import jwt from 'jsonwebtoken'; 106 | import { loginRedirectUrl } from '../auth0'; 107 | try { 108 | // get the 'claims', information about who the user is 109 | // claiming to be. this is also where we verify the signature 110 | const claims = jwt.verify( 111 | token, 112 | // this here loads the public key of our instance from a file on disk 113 | fs.readFileSync((runtime.CERT_PATH as string) || './cert-dev.pem') 114 | ); 115 | // no error thrown, token is valid 116 | event.context.claims = claims; 117 | event.context.user = await event.context.client.findFirst({ 118 | where: {email: claims.email,} 119 | }); 120 | } catch (e) { 121 | // token may be malformed or expired, so clear cookies 122 | // and redirect them to auth0 login 123 | console.error(e); 124 | setCookie(event, 'token', ''); 125 | return await sendRedirect(event, loginRedirectUrl()); 126 | } 127 | ``` 128 | -------------------------------------------------------------------------------- /architecture_and_systems/component_based_design.md: -------------------------------------------------------------------------------- 1 | # Component Based Design 2 | 3 | In web development, components are reusable, self-contained building blocks that make up the user interface of a web application. They encapsulate both the structure and behavior of a specific part of a webpage, allowing developers to create complex interfaces by combining and nesting these components. 4 | 5 | ## Reusability 6 | 7 | Components can be easily reused, reducing the overall amount of code that has to be written and - especially - read. For example, instead of defining a card each time it is used, you can create a `Card` component that can be reused with properties. 8 | 9 | React Example: 10 | 11 | Without Componentization: 12 | 13 | ```jsx 14 | const App = () => { 15 | return ( 16 |
17 |
18 |
19 |
20 |

Title 1

21 |
22 |
23 |

Body 1

24 |
25 |
26 |
27 |
28 |

Title 2

29 |
30 |
31 |

Body 2

32 |
33 |
34 |
35 |
36 |

Title 3

37 |
38 |
39 |

Body 3

40 |
41 |
42 |
43 |
44 | ); 45 | }; 46 | 47 | export default App; 48 | ``` 49 | 50 | With Componentization: 51 | 52 | ```jsx 53 | const Card = ({ title, body }) => { 54 | return ( 55 |
56 |
57 |

{title}

58 |
59 |
60 |

{body}

61 |
62 |
63 | ); 64 | }; 65 | 66 | export default Card; 67 | ``` 68 | 69 | ```jsx 70 | import Card from "./Card"; 71 | 72 | const App = () => { 73 | return ( 74 |
75 |
76 | 77 | 78 | 79 |
80 |
81 | ); 82 | }; 83 | 84 | export default App; 85 | ``` 86 | 87 | Vue Example: 88 | 89 | Without Componentization: 90 | 91 | ```html 92 | 122 | ``` 123 | 124 | With Componentization: 125 | 126 | ```html 127 | 136 | 137 | 140 | ``` 141 | 142 | In both examples, the first section shows the code without componentization, where the card structure is defined directly inside the grid. In the second section, a Card component is created that accepts props for the title and body of the card. This component is then used inside the parent component to create three instances of the card. 143 | 144 | This concept can be applied to any UI element: cards, modals, inputs, dropdowns, layouts, etc. 145 | 146 | > Note from Taz: it's easy to go overboard with componentization. Sometimes it's easier to have one component that can be customized by passing in some props rather than creating a component for every possible variation. You can also break a component up into sub components, allowing the component to be built as needed out of its individual parts instead of only usable as an aggregate. See [Headless UI](https://headlessui.com/) for inspiration. 147 | 148 | ## Ease of Updating & Testing 149 | 150 | Component based design makes it easy to update everything. Instead of having to make same change for every instance, you can make the change in one spot and have it automatically affect all usages. For example, if we wanted to add a class to the `h2` tag. Without componentization, we have to modify every instance. With componentization, we simply modify the `Card` component. 151 | 152 | It is also easier to create tests for individual components and they can be tested in parallel, massively improving the speed with which tests can be run. 153 | 154 | ## Component Props & Events 155 | 156 | Props and events are two important mechanisms used in component-based web development frameworks, such as React or Vue, to facilitate communication between components. 157 | 158 | Props, short for properties, are used to pass data from a parent component to its child component. They allow the parent component to customize and configure the child component by passing values, objects, or functions as props. The child component can then access and use these props in its template or logic to render dynamic content or perform specific actions. Props are typically immutable, meaning they cannot be modified by the child component. 159 | 160 | Events, on the other hand, are used to allow child components to communicate with their parent components. Child components can emit events, such as a button click or a form submission, to notify their parent components about certain actions or state changes. The parent component can listen to these events and respond accordingly by executing specific methods or updating its own state. 161 | 162 | The combination of props and events enables a flexible and modular approach to building web applications. By passing data down through props, components can be easily reused and composed in different contexts, promoting code reusability and maintainability. On the other hand, events enable components to be interactive and communicate with each other, creating a dynamic and responsive user interface. 163 | 164 | ### React Example 165 | 166 | ```jsx 167 | // Parent Component 168 | import React, { useState } from "react"; 169 | import ChildComponent from "./ChildComponent"; 170 | 171 | const ParentComponent = () => { 172 | const [name, setName] = useState(""); 173 | const [age, setAge] = useState(0); 174 | const [counter, setCounter] = useState(0); 175 | 176 | const handleNameChange = e => { 177 | setName(e.target.value); 178 | }; 179 | 180 | const handleAgeChange = e => { 181 | setAge(parseInt(e.target.value)); 182 | }; 183 | 184 | const incrementCounter = () => { 185 | setCounter(prevCounter => prevCounter + 1); 186 | }; 187 | 188 | return ( 189 |
190 |

Parent Component

191 | 192 | 193 |

Counter: {counter}

194 | 200 |
201 | ); 202 | }; 203 | 204 | export default ParentComponent; 205 | ``` 206 | 207 | ```jsx 208 | // Child Component 209 | 210 | const ChildComponent = ({ name, age, onIncrementCounter }) => { 211 | return ( 212 |
213 |

Child Component

214 |

Name: {name}

215 |

Age: {age}

216 | 217 | 218 |
219 | ); 220 | }; 221 | 222 | export default ChildComponent; 223 | ``` 224 | 225 | ### Vue Example 226 | 227 | ```html 228 | 239 | 240 | 252 | ``` 253 | 254 | ```html 255 | // ChildComponent.vue 256 | 265 | 266 | 272 | ``` 273 | -------------------------------------------------------------------------------- /architecture_and_systems/data_flow.md: -------------------------------------------------------------------------------- 1 | # Data Flow in Networked & Local Applications 2 | 3 | ## Server and Database on Separate Machines 4 | 5 | ```mermaid 6 | graph LR 7 | A[Client] --> B[Server] 8 | B --> C[Database] 9 | ``` 10 | 11 | In this diagram, the client sends a request to the server, which then communicates with the database on a separate machine to retrieve or update the data. 12 | 13 | ## Server and Database on the Same Machine 14 | 15 | ```mermaid 16 | graph LR 17 | A[Client] --> B[Server & Database] 18 | ``` 19 | 20 | In this diagram, both the server and the database are running on the same machine. The client sends a request to the server, which directly accesses the database on the same machine to handle the request. 21 | 22 | ## Local Application Accessing Local Data 23 | 24 | ```mermaid 25 | graph LR 26 | A[Application] --> B[Data] 27 | ``` 28 | 29 | In this diagram, there are two separate boxes representing the application and the data on the same machine. The local application directly accesses the local data without any network communication. 30 | 31 | ## Client-Server-Database Communication 32 | 33 | In the following diagram, the client initiates the data flow by sending a request to the server. The server then retrieves the requested data from the database. Once the database returns the data to the server, the server sends it back to the client. 34 | 35 | ```mermaid 36 | sequenceDiagram 37 | participant Client 38 | participant Server 39 | participant Database 40 | 41 | Client->>Server: Request data 42 | Server->>Database: Retrieve data 43 | Database->>Server: Return data 44 | Server->>Client: Send data 45 | 46 | ``` 47 | 48 | ## Making Network Requests in JavaScript 49 | 50 | > Note from Taz: make sure to review [the notes on async/await](../languages_and_frameworks/webdev_pathway.md#asyncawait--promises) 51 | 52 | ```javascript 53 | async function fetchData() { 54 | try { 55 | // by default a request will be a GET request. 56 | const response = await fetch("https://api.example.com/data"); 57 | const data = await response.json(); 58 | 59 | // Use the retrieved data here 60 | console.log(data); 61 | } catch (error) { 62 | // Handle any errors that occurred during the request 63 | console.error(error); 64 | } 65 | } 66 | ``` 67 | 68 | This example is generally used solely to retrieve data from a server. In order to send data, we can use either a POST or a PUT request depending on what kind of operation we are performing. POST requests generally imply creation of something, and PUT requests generally imply updating something. 69 | 70 | The following example demonstrates how we would use the fetch API to send a POST request containing JSON. 71 | 72 | ```js 73 | async function postData() { 74 | try { 75 | const url = "https://api.example.com/data"; 76 | const data = { 77 | name: "John Doe", 78 | age: 25, 79 | email: "johndoe@example.com", 80 | }; 81 | 82 | const response = await fetch(url, { 83 | method: "POST", 84 | headers: { 85 | "Content-Type": "application/json", 86 | }, 87 | body: JSON.stringify(data), 88 | }); 89 | 90 | if (response.ok) { 91 | const responseData = await response.json(); 92 | console.log(responseData); 93 | } else { 94 | throw new Error("Request failed with status " + response.status); 95 | } 96 | } catch (error) { 97 | // Handle any errors that occurred during the request 98 | console.error(error); 99 | } 100 | } 101 | ``` 102 | 103 | Using network requests in React or Next requires special considerations as they must be wrapped in useEffect if they are going to be fetching data after the component has rendered, such as on mount of the component. Usage in Vue and Nuxt is straightforward, essentially the same as in vanilla JavaScript, but Nuxt gives us some extra quality of life features, such as useFetch for loading data with automatic serverside support and $fetch as a more convenient wrapper for fetch. 104 | 105 | > Note from Taz: you CANNOT use async inside of a `useEffect`. You have to define an async function inside the `useEffect` that does what you want with whatever `await`s you need inside of the function, and then call it inside the `useEffect`. In Vue, you can use `await` anywhere except a `computed`. 106 | 107 | An example for sending data in React: 108 | 109 | ```js 110 | import React, { useState } from "react"; 111 | 112 | function MyComponent() { 113 | const [formData, setFormData] = useState({ name: "", email: "" }); 114 | const [responseData, setResponseData] = useState(null); 115 | 116 | const handleSubmit = async e => { 117 | e.preventDefault(); 118 | 119 | try { 120 | const response = await fetch("https://api.example.com/data", { 121 | method: "POST", 122 | headers: { 123 | "Content-Type": "application/json", 124 | }, 125 | body: JSON.stringify(formData), 126 | }); 127 | const data = await response.json(); 128 | setResponseData(data); 129 | } catch (error) { 130 | console.error(error); 131 | } 132 | }; 133 | 134 | const handleInputChange = e => { 135 | setFormData({ ...formData, [e.target.name]: e.target.value }); 136 | }; 137 | 138 | return ( 139 |
140 |
141 | 147 | 153 | 154 |
155 | {responseData &&
Response: {JSON.stringify(responseData)}
} 156 |
157 | ); 158 | } 159 | ``` 160 | 161 | The same example in Vue: 162 | 163 | ```html 164 | 174 | 175 | 199 | ``` 200 | 201 | Next and Nuxt provide mechanisms for fetching the required data before a page is sent to the client. This is called server-side rendering, where the server will make any database calls it can before sending the page so that the client does not have to make extra requests after loading the page. This also enables Search Engine Optimization (SEO) for the content, as search engines will be able to index the content when the page loads. For details on these mechanisms, checkout the relevant documentation for [Nuxt](https://nuxtjs.org/guide/features/fetch) and [Next](https://nextjs.org/docs/pages/building-your-application/data-fetching) websites. 202 | 203 | For more information on using the fetch API, see the [corresponding MDN pages](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch). 204 | 205 | ## Making Network Requests in Python 206 | 207 | Below is another example of making a POST request, but in Python. 208 | 209 | ```python 210 | import requests 211 | import json 212 | 213 | def post_data(): 214 | try: 215 | url = 'https://api.example.com/data' 216 | data = { 217 | 'name': 'John Doe', 218 | 'age': 25, 219 | 'email': 'johndoe@example.com' 220 | } 221 | headers = { 222 | 'Content-Type': 'application/json' 223 | } 224 | 225 | response = requests.post(url, data=json.dumps(data), headers=headers) 226 | 227 | if response.ok: 228 | response_data = response.json() 229 | print(response_data) 230 | else: 231 | response.raise_for_status() 232 | 233 | except requests.exceptions.RequestException as error: 234 | # Handle any errors that occurred during the request 235 | print(error) 236 | ``` 237 | 238 | For more information on making requests in Python, see the [requests documentation.](https://requests.readthedocs.io/en/latest/) 239 | 240 | ## Structure of an URL 241 | ![image](https://github.com/UTDallasEPICS/Guides/assets/35643616/8156653c-3f4c-4c1a-8d57-ddbc267aa608) 242 | -------------------------------------------------------------------------------- /architecture_and_systems/databases.md: -------------------------------------------------------------------------------- 1 | # Databases 2 | 3 | ## SQL 4 | 5 | SQL (Structured Query Language) databases, such as MySQL and PostgreSQL (often referred to as Postgres), are widely used for managing and organizing large amounts of data. SQL is a standardized language used to communicate with these databases. 6 | 7 | At a high level, SQL databases consist of tables that store data in a structured manner. Each table is made up of rows and columns, where each row represents a record and each column represents a specific attribute or field of that record. For example, in a database of employees, a table might have columns for employee ID, name, age, and department. 8 | 9 | SQL provides a set of commands, also known as queries, to interact with the database. These queries can be used to create, retrieve, update, and delete data. Here are a few examples: 10 | 11 | ### Creating a table: 12 | 13 | ```sql 14 | CREATE TABLE Employees ( 15 | ID INT PRIMARY KEY, 16 | Name VARCHAR(50), 17 | Age INT, 18 | Department VARCHAR(50) 19 | ); 20 | ``` 21 | 22 | ### Inserting data into a table: 23 | 24 | ```sql 25 | INSERT INTO Employees (ID, Name, Age, Department) 26 | VALUES (1, 'John Doe', 25, 'Sales'); 27 | ``` 28 | 29 | ### Retrieving data from a table: 30 | 31 | ```sql 32 | SELECT * FROM Employees; 33 | ``` 34 | 35 | ### Updating data in a table: 36 | 37 | ```sql 38 | UPDATE Employees SET Age = 26 WHERE ID = 1; 39 | ``` 40 | 41 | ### Deleting data from a table: 42 | 43 | ```sql 44 | DELETE FROM Employees WHERE ID = 1; 45 | ``` 46 | 47 | ## PostgreSQL & MySQL 48 | 49 | These are two of the most popular SQL databases. They both use SQL, with some minor differences. For most real world business applications (in terms of number of businesses, not annual revenues - most businesses are not Google and don't have Google sized problems), they are largely interchangeable. For some applications, PostgreSQL dominates MySQL in terms of extensibility and flexibility. PostgreSQL supports the installation of extensions, such as pg_vector which adds support for vector searching or postgresml which adds support for running machine learning directly in the database. 50 | 51 | ## MongoDB 52 | 53 | MongoDB is a popular NoSQL database that differs from traditional SQL databases in its approach to data storage and retrieval. It is designed to handle large-scale, unstructured, and semi-structured data, making it well-suited for certain types of applications. 54 | 55 | MongoDB stores data in flexible, JSON-like documents, known as BSON (Binary JSON). These documents can have varying structures, allowing for dynamic schema and easy scalability. Instead of using tables and rows like SQL databases, MongoDB organizes data into collections of documents. 56 | 57 | MongoDB and NoSQL generally were designed for a very specific set of problems faced by hyperscalers such as Meta and Google. The vast majority of business applications benefit more from the relational model and ease of accessibility to the business people. Business people that are involved in analytics know SQL, which is designed to be close to natural language. They probably don't know Javascript. SQL is easier for laypeople to read than the code needed to achieve similar results with MongoDB. Business people are the ones who know how the money works, it's a good idea to be able to work well with them. It's also easier to write custom queries in SQL if you need to work directly with the database. 58 | 59 | Additionally, the lack of a fixed schema in MongoDB means that the complexity of guaranteeing data structures moves from the database to being managed by the developer in code. You still need and end up with a schema, you just have to do more work for it. Note that with SQL, you can still use an ORM to get an interface that works better with code, such as Prisma or Drizzle (for the JS ecosystem), so using an SQL database gets you the best of both worlds. 60 | 61 | Both NoSQL and SQL ultimately ended up integrating a lot of each others features. MongoDB implemented better support for a lot of SQL features, and SQL systems integrated better horizontal scaling and sharding support. 62 | 63 | > This is Taz speaking direct from personal experience. I used MongoDB for my very first EPICS project back in 2016, a data dashboard for TexProtects, and had to write data aggregation queries using MongoDBs JS library. 64 | 65 | > Version 2 used Postgres. 66 | 67 | An SQL analytics query: 68 | 69 | ```sql 70 | SELECT Customers.CustomerID, Customers.CustomerName, SUM(Orders.TotalAmount) AS TotalSales 71 | FROM Customers 72 | INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID 73 | WHERE Orders.OrderDate >= '2022-01-01' AND Orders.OrderDate <= '2022-12-31' 74 | GROUP BY Customers.CustomerID, Customers.CustomerName 75 | ORDER BY TotalSales DESC 76 | LIMIT 10; 77 | 78 | ``` 79 | 80 | The same query using MongoDB: 81 | 82 | ```js 83 | const customersCollection = db.collection("customers"); 84 | const ordersCollection = db.collection("orders"); 85 | 86 | const results = customersCollection.aggregate([ 87 | { 88 | $lookup: { 89 | from: "orders", 90 | localField: "CustomerID", 91 | foreignField: "CustomerID", 92 | as: "orders", 93 | }, 94 | }, 95 | { 96 | $unwind: "$orders", 97 | }, 98 | { 99 | $match: { 100 | "orders.OrderDate": { 101 | $gte: new Date("2022-01-01"), 102 | $lte: new Date("2022-12-31"), 103 | }, 104 | }, 105 | }, 106 | { 107 | $group: { 108 | _id: { 109 | CustomerID: "$CustomerID", 110 | CustomerName: "$CustomerName", 111 | }, 112 | TotalSales: { $sum: "$orders.TotalAmount" }, 113 | }, 114 | }, 115 | { 116 | $sort: { TotalSales: -1 }, 117 | }, 118 | { 119 | $limit: 10, 120 | }, 121 | ]); 122 | 123 | results.toArray((err, docs) => { 124 | if (err) { 125 | console.error(err); 126 | } else { 127 | console.log(docs); 128 | } 129 | }); 130 | ``` 131 | 132 | Prisma (an ORM for JavaScript that supports many databases, including MySQL, PostgreSQL, and MongoDB): 133 | 134 | ```js 135 | const customers = await prisma.customer.findMany({ 136 | select: { 137 | CustomerID: true, 138 | CustomerName: true, 139 | TotalSales: { 140 | sum: { 141 | select: { 142 | TotalAmount: true, 143 | }, 144 | }, 145 | }, 146 | }, 147 | where: { 148 | orders: { 149 | some: { 150 | OrderDate: { 151 | gte: new Date("2022-01-01"), 152 | lte: new Date("2022-12-31"), 153 | }, 154 | }, 155 | }, 156 | }, 157 | orderBy: { 158 | TotalSales: "desc", 159 | }, 160 | take: 10, 161 | }); 162 | console.log(customers); 163 | ``` 164 | 165 | The SQL query is much easier for non-software people to read and understand, because it reads like English, with verbs and nouns and a grammatical structure. SQL plus an ORM like Prisma enables you to speak engineering while the business people speak SQL. MongoDB enables you to speak engineering and your business people to be confused. You can however use MongoDB for other applications such as caching, where the business people are unlikely to touch it and speed dominates all other concerns. 166 | 167 | ## Redis 168 | 169 | However, for many cases there are also better solutions than NoSQL for caching. One of these is Redis, an in-memory database that read/writes from RAM instead of storage. 170 | 171 | > Another side note from Taz. It's important to understand how computer hardware and networking work. You can do anything in software, but how well it performs in the real world is dependent upon the hardware you are running on. You don't need to know how to fabricate a chip, but you do need to know the difference between and implications of storage vs RAM; networking connectivity such as Ethernet, WiFi, and 5G; how processors handle multitasking; local vs cloud vs edge; protocols like HTTPS, SSH, and IP. 172 | 173 | > For this section, all you need to know is that reading from storage is slow and reading from RAM is fast. In most cases these days storage is an SSD (solid state drive), in some cases still platter drives (spinning disks with read/write heads), and in some very specific cases even magnetic tape drives (like VHS tapes but the size of washing machines). 174 | 175 | Because Redis read/writes from memory, it is very, very, very fast. However, memory space costs significantly more than storage and RAM is technically ephemeral storage, meaning that data is not persisted to disk by default (if the machine turns off, data is lost). This means Redis is best for applications where you need very high throughput for relatively little data that doesn't require persistence guarantees, such as caching user sessions. You probably won't need this for an EPICS project but you will definitely run into it and its siblings (such as Memcached) in your career. 176 | 177 | ## Using Docker 178 | 179 | For local development, Docker makes it convenient to have multiple databases running with total isolation from each other. All databases have a Docker image that you can configure and run. This is easier for Linux and Mac users - Windows requires a bit more work to add the Windows Subsystem for Linux. Microsoft realized they sucked at making an operating system for software engineering and admitted that they needed Linux in their lives, so they added support for running the Linux kernel without having to dualboot or use a virtual machine. For example, see the [PostgreSQL image on Docker Hub](https://hub.docker.com/_/postgres/). 180 | -------------------------------------------------------------------------------- /architecture_and_systems/frontend_vs_backend_routing.md: -------------------------------------------------------------------------------- 1 | # Frontend vs Backend Routing 2 | 3 | API routes and routes in frontend frameworks like Vue and React serve different purposes. Both of them will show up in your browser history and URL bar, but only the API routes actually exist from the perspective of the server if you are building an SPA (unless you are using SSR or statically served content). 4 | 5 | API routes refer to the endpoints on the server that allow clients to access and interact with data or services. They define the server-side logic that handles requests and returns responses in a structured format, usually JSON or XML. API routes are responsible for managing the backend functionality, such as retrieving, creating, updating, or deleting data. They may or may not be involved in user navigation, depending on system architecture. 6 | 7 | On the other hand, routes in frontend frameworks like Vue and React define the client-side navigation and component rendering within a single-page application (SPA). They determine how the application's UI is structured and how different components are rendered based on the URL or user interactions. These routes are responsible for managing the frontend user experience, navigation, and rendering of components. These usually directly affect the users's browser history. 8 | 9 | This can cause some interesting side effects in an SPA. Because frontend routing updates the users history in the browser and the URL in the browser bar, users often end up trying to directly load a frontend route in an SPA which results in a 404 because the backend does not have that corresponding route. In an SSR application, this is not an issue because the backend handles rendering the corresponding application page anyways. 10 | 11 | When handling redirects, some cases are achievable through frontend alone, while other require a request to be sent to the server so the server can handle the redirect. The former can be achieved by directly setting the location of a window: 12 | 13 | ```js 14 | window.location.href = "/about"; // goes to current_domain/about 15 | window.location.href = "https://google.com"; // goes to the google.com domain 16 | ``` 17 | 18 | For server redirects, you must direct the user to the API route that is responsible for handling the redirect. A common example of this is handling logouts, where you may redirect a user to `/api/logout`, and the backend route at that location handles cleanup such as clearing cookies or generating authentication tokens before forwarding the user on to a new domain. 19 | -------------------------------------------------------------------------------- /architecture_and_systems/state_management.md: -------------------------------------------------------------------------------- 1 | ## State Management 2 | 3 | State management is a crucial concept in web development, especially in frameworks like React and Vue. It helps in breaking data storage and functions out of components, making them accessible across multiple components as reusable functions. In React, this is commonly achieved using hooks and Redux, while in Vue, it can be done using composables and Pinia with composition syntax. 4 | 5 | ### React 6 | 7 | React provides a built-in state management solution called hooks, which allow you to manage state within functional components. The most commonly used hook for state management is the `useState` hook. It allows you to declare and update state variables within functional components. 8 | 9 | Here's an example of using `useState` in React: 10 | 11 | ```jsx 12 | import React, { useState } from "react"; 13 | 14 | const Counter = () => { 15 | const [count, setCount] = useState(0); 16 | 17 | const increment = () => { 18 | setCount(count + 1); 19 | }; 20 | 21 | return ( 22 |
23 |

Count: {count}

24 | 25 |
26 | ); 27 | }; 28 | 29 | export default Counter; 30 | ``` 31 | 32 | In this example, we use the `useState` hook to declare a state variable `count` and a function `setCount` to update its value. The `increment` function increases the count when the button is clicked. 33 | 34 | You can also create your own hooks, as shown below: 35 | 36 | ```jsx 37 | import { useState, useEffect } from "react"; 38 | 39 | function useFetch(url) { 40 | const [data, setData] = useState(null); 41 | const [isLoading, setLoading] = useState(true); 42 | const [error, setError] = useState(null); 43 | 44 | useEffect(() => { 45 | async function fetchData() { 46 | try { 47 | const response = await fetch(url); 48 | const json = await response.json(); 49 | setData(json); 50 | setLoading(false); 51 | } catch (error) { 52 | setError(error); 53 | setLoading(false); 54 | } 55 | } 56 | 57 | fetchData(); 58 | }, [url]); 59 | 60 | return { data, isLoading, error }; 61 | } 62 | 63 | function App() { 64 | const { data, isLoading, error } = useFetch("https://api.example.com/data"); 65 | 66 | if (isLoading) { 67 | return
Loading...
; 68 | } 69 | 70 | if (error) { 71 | return
Error: {error.message}
; 72 | } 73 | 74 | return ( 75 |
76 | {data && ( 77 |
    78 | {data.map(item => ( 79 |
  • {item.name}
  • 80 | ))} 81 |
82 | )} 83 |
84 | ); 85 | } 86 | 87 | export default App; 88 | ``` 89 | 90 | Now, let's talk about Redux, a popular state management library for React. Redux allows you to manage the state of your entire application in a central store. It follows a unidirectional data flow and provides a predictable way to handle state changes. 91 | 92 | To use Redux, you need to define actions, reducers, and a store. Actions represent the events that can occur in your application, reducers handle those actions and update the state, and the store holds the state of your application. 93 | 94 | Here's a simplified example of using Redux in React: 95 | 96 | 1. Define an action: 97 | 98 | ```jsx 99 | const increment = () => { 100 | return { 101 | type: "INCREMENT", 102 | }; 103 | }; 104 | ``` 105 | 106 | 2. Define a reducer: 107 | 108 | ```jsx 109 | const counterReducer = (state = 0, action) => { 110 | switch (action.type) { 111 | case "INCREMENT": 112 | return state + 1; 113 | default: 114 | return state; 115 | } 116 | }; 117 | ``` 118 | 119 | 3. Create a store: 120 | 121 | ```jsx 122 | import { createStore } from "redux"; 123 | import counterReducer from "./reducers/counterReducer"; 124 | 125 | const store = createStore(counterReducer); 126 | ``` 127 | 128 | 4. Connect your components to the store: 129 | 130 | ```jsx 131 | import React from "react"; 132 | import { connect } from "react-redux"; 133 | import { increment } from "./actions/counterActions"; 134 | 135 | const Counter = ({ count, increment }) => { 136 | return ( 137 |
138 |

Count: {count}

139 | 140 |
141 | ); 142 | }; 143 | 144 | const mapStateToProps = state => { 145 | return { 146 | count: state, 147 | }; 148 | }; 149 | 150 | const mapDispatchToProps = { 151 | increment, 152 | }; 153 | 154 | export default connect(mapStateToProps, mapDispatchToProps)(Counter); 155 | ``` 156 | 157 | In this example, we define an action `increment`, a reducer `counterReducer`, and create a store using `createStore` from Redux. Then, we connect our component to the store using the `connect` function from `react-redux`. The `mapStateToProps` function maps the state from the store to props, and `mapDispatchToProps` maps the actions to props. 158 | 159 | ### Vue 160 | 161 | To break out functionality in Vue, we can use composables. 162 | 163 | ```js 164 | export function useFetch(url) { 165 | const data = ref(null); 166 | const isLoading = ref(true); 167 | const error = ref(null); 168 | 169 | onMounted(async () => { 170 | try { 171 | const response = await fetch(url); 172 | const json = await response.json(); 173 | data.value = json; 174 | isLoading.value = false; 175 | } catch (error) { 176 | error.value = error; 177 | isLoading.value = false; 178 | } 179 | }); 180 | 181 | return { 182 | data, 183 | isLoading, 184 | error, 185 | }; 186 | } 187 | ``` 188 | 189 | ```html 190 | 199 | 200 | 205 | ``` 206 | 207 | To manage state in Vue, we can create our own simple composables for sharing functionality, and Pinia for managing shared data. 208 | 209 | 1. Define a store: 210 | 211 | ```javascript 212 | import { defineStore } from "pinia"; 213 | 214 | export const useCounterStore = defineStore("counter", () => { 215 | const count = ref(0); 216 | 217 | const increment = () => { 218 | count.value++; 219 | }; 220 | 221 | return { 222 | count, 223 | increment, 224 | }; 225 | }); 226 | ``` 227 | 228 | In this example, we define a store called `counter` using `defineStore` from Pinia. Inside the store, we use the Composition API to define a reactive state variable `count` initialized to 0. We also define an `increment` function that increments the `count` value. 229 | 230 | 2. Use the store in a component: 231 | 232 | ```vue 233 | 239 | 240 | 248 | ``` 249 | -------------------------------------------------------------------------------- /architecture_and_systems/users_roles_and_permissions.md: -------------------------------------------------------------------------------- 1 | # Users, Roles, and Permissions 2 | 3 | ## Organizing user information 4 | 5 | For this section we will be using the following schema: 6 | 7 | ```prisma 8 | model User { 9 | id Int @id @default(autoincrement()) 10 | username String @unique 11 | email String @unique 12 | password String 13 | 14 | EmployeeProfile EmployeeProfile? @relation(fields: [employeeProfileId], references: [id]) 15 | employeeProfileId Int? 16 | CustomerProfile CustomerProfile? @relation(fields: [customerProfileId], references: [id]) 17 | customerProfileId Int? 18 | } 19 | 20 | model EmployeeProfile { 21 | id Int @id @default(autoincrement()) 22 | start_date DateTime @default(now()) 23 | end_date DateTime? 24 | 25 | Role Role @relation(fields: [roleId], references: [id]) 26 | roleId Int 27 | User User @relation(fields: [userId], references: [id]) 28 | userId Int? 29 | } 30 | 31 | model CustomerProfile { 32 | id Int @id @default(autoincrement()) 33 | birth_date DateTime @default(now()) 34 | 35 | User User @relation(fields: [userId], references: [id]) 36 | userId Int? 37 | } 38 | 39 | model Role { 40 | id Int @id @default(autoincrement()) 41 | name String @unique 42 | title String @unique 43 | 44 | users User[] 45 | } 46 | 47 | ``` 48 | 49 | We use one user table to handle all logins. This makes login and authentication checking very straightforward. To identify what kind of user a user is, we can check if it possesses a profile. 50 | 51 | ```js 52 | const email = "example@example.com"; 53 | const user = await prisma.user.findFirst({ 54 | where: { email }, 55 | include: { 56 | CustomerProfile: true, 57 | EmployeeProfile: true, 58 | }, 59 | }); 60 | if (user.EmployeeProfile) { 61 | // user is an employee 62 | } 63 | if (user.CustomerProfile) { 64 | // user is a customer 65 | } 66 | ``` 67 | 68 | Note that our `Role` table is connected to the `EmployeeProfile` table, not the `User` table. This because not all Users have roles, but all Employees have roles. If all users had roles, we could put the relationship on the `User` table, but it's likely Customers and Employees would have different types of roles conceptually, and we might want to keep those defined separately as CustomerRoles and EmployeeRoles anyways. 69 | 70 | To check that a User is an Employee and is an Admin, we can do the following: 71 | 72 | ```js 73 | const email = "example@example.com"; 74 | const user = await prisma.user.findFirst({ 75 | where: { email }, 76 | include: { 77 | CustomerProfile: true, 78 | EmployeeProfile: true, 79 | }, 80 | }); 81 | if (user.EmployeeProfile && user.EmployeeProfile.name == "admin") { 82 | return `User is an Admin`; 83 | } 84 | return `User is not an Admin`; 85 | ``` 86 | 87 | In this example, there is only one store and all users belong to it. If we have to track multiple stores, we could theoretically have Employees and Customers that have different roles at different stores. We would then have a join table in our schema like so: 88 | 89 | ```prisma 90 | model Store { 91 | id Int @default(autoincrement()) 92 | name String 93 | UserToStore UserToStore[] 94 | } 95 | model UserToStore { 96 | id Int @default(autoincrement()) 97 | start_date DateTime @default(now()) 98 | end_date DateTime? 99 | 100 | User User @relation(fields: [userId], references: [id]) 101 | userId Int 102 | Store Store @relation(fields: [storeId], references: [id]) 103 | storeId Int 104 | Role Role @relation(fields:[roleId], references: [id]) 105 | roleId Int 106 | 107 | @@unique([userId, storeId, roleId]) 108 | } 109 | ``` 110 | 111 | The information about a User's role is now dependent on which Store the User is at, because it is on the table for the **relationship** between a User and a Store. Also note that there can only be one entry in the database for a combination of `userId`, `storeId`, and `roleId`. 112 | 113 | The previous examples use role based permissions, where having a role automatically grants a user a large swathe of permissions. Alternatively we can assign permissions granularly, where each user can have a completely customized level of access. We can model this like so: 114 | 115 | ```prisma 116 | model UserToStore{ 117 | id Int @id @default(autoincrement()) 118 | start_date DateTime @default(now()) 119 | end_date DateTime? 120 | // each specific possible operation has its own permissions flag 121 | can_open_registers Boolean @default(false) 122 | can_receive_shipments Boolean @default(false) 123 | can_process_refunds Boolean @default(false) 124 | 125 | User User @relation(fields: [userId], references: [id]) 126 | userId Int 127 | Store Store @relation(fields: [storeId], references: [id]) 128 | storeId Int 129 | } 130 | 131 | ``` 132 | -------------------------------------------------------------------------------- /demos/html_css_js/README.md: -------------------------------------------------------------------------------- 1 | # HTML/CSS/JS Demo 2 | 3 | This demo consists of a single HTML file with some CSS and JS. It demonstrates the usage of HTML for semantic markup of the document, CSS for making things look pretty, and JS for some buttons. 4 | 5 | To run this demo, open the index.html file in any browser. 6 | 7 | # Concepts to note 8 | 9 | - Structure of elements - child elements, sibling elements 10 | - usage of classes vs styles 11 | - CSS cascading of style based on selectors 12 | - JS adding an event listener to an HTML element 13 | - JS modifying the HTML in the browser to display data 14 | -------------------------------------------------------------------------------- /demos/html_css_js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | HTML, CSS, and JS Demo 8 | 132 | 133 | 134 | 135 |

Welcome to HTML, CSS, and JS Demo!

136 |

This is a demonstration of basic concepts.

137 |

Highlighted text using a CSS class.

138 |

Big text using style attribute.

139 | 140 | 141 |
142 |

Counter: 0

143 | 144 |
145 | 146 | 147 |
148 | 149 | 150 |

151 |
152 | 153 | 154 |
155 |

This box slides in from the right using CSS animations.

156 |
157 | 158 | 159 |
160 |
Flex Item 1
161 |
Flex Item 2
162 |
Flex Item 3
163 |
164 | 165 | 166 |
167 |
Grid Item 1
168 |
Grid Item 2
169 |
Grid Item 3
170 |
Grid Item 4
171 |
Grid Item 5
172 |
Grid Item 6
173 |
174 | 175 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /demos/react-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC" 12 | } 13 | -------------------------------------------------------------------------------- /demos/servers/.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL=file:./db.db -------------------------------------------------------------------------------- /demos/servers/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /demos/servers/README.md: -------------------------------------------------------------------------------- 1 | # Server & Database Demo 2 | 3 | Install dependencies with `npm i` 4 | 5 | Make sure to generate the Prisma library with `npx prisma generate` 6 | 7 | This repository contains a SQLite database file that has already been migrated to use the schema in `prisma/schema.prisma` and the `.env` file is already configured with the location of the database 8 | 9 | To run the server, `node index.js` and navigate to `http://localhost:3000` in your browser 10 | -------------------------------------------------------------------------------- /demos/servers/db.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UTDallasEPICS/Guides/9419fbc43367f73efe1d83e8bddd5f3197c0e236/demos/servers/db.db -------------------------------------------------------------------------------- /demos/servers/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { PrismaClient } = require("@prisma/client"); 3 | 4 | const prisma = new PrismaClient(); 5 | const app = express(); 6 | const path = require("path"); 7 | 8 | // Automatically convert incoming requests with the 'Content-Type: application/json' header to JSON objects 9 | app.use(express.json()); 10 | 11 | // Setup our static file serving 12 | app.use(express.static(path.join(__dirname, "public"))); 13 | 14 | // Make sure the base route returns the index.html 15 | app.get("/", (req, res) => { 16 | res.sendFile(path.join(__dirname, "public", "index.html")); 17 | }); 18 | 19 | // Create todo 20 | app.post("/todos", async (req, res) => { 21 | const todo = await prisma.todo.create({ 22 | data: { 23 | title: req.body.title, 24 | content: req.body.content, 25 | }, 26 | }); 27 | res.json(todo); 28 | }); 29 | 30 | // Update todo 31 | app.put("/todos/:id", async (req, res) => { 32 | const todo = await prisma.todo.update({ 33 | // when we use the :label syntax in the route definition, those parts of the url 34 | // are made available as properties on req.params 35 | where: { id: req.params.id }, 36 | data: req.body, 37 | }); 38 | res.json(todo); 39 | }); 40 | 41 | // Get all todos 42 | app.get("/todos", async (req, res) => { 43 | const todos = await prisma.todo.findMany(); 44 | res.json(todos); 45 | }); 46 | 47 | // Delete single todo 48 | app.delete("/todos/:id", async (req, res) => { 49 | const todo = await prisma.todo.delete({ 50 | where: { id: req.params.id }, 51 | }); 52 | res.json(todo); 53 | }); 54 | 55 | // Start listening 56 | app.listen(3000, () => { 57 | console.log("Server started on http://localhost:3000"); 58 | }); 59 | -------------------------------------------------------------------------------- /demos/servers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "servers", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@prisma/client": "^5.19.1", 14 | "express": "^4.19.2", 15 | "prisma": "^5.19.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /demos/servers/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | dependencies: 4 | '@prisma/client': 5 | specifier: ^5.19.1 6 | version: 5.19.1(prisma@5.19.1) 7 | express: 8 | specifier: ^4.19.2 9 | version: 4.19.2 10 | prisma: 11 | specifier: ^5.19.1 12 | version: 5.19.1 13 | 14 | packages: 15 | 16 | /@prisma/client@5.19.1(prisma@5.19.1): 17 | resolution: {integrity: sha512-x30GFguInsgt+4z5I4WbkZP2CGpotJMUXy+Gl/aaUjHn2o1DnLYNTA+q9XdYmAQZM8fIIkvUiA2NpgosM3fneg==} 18 | engines: {node: '>=16.13'} 19 | requiresBuild: true 20 | peerDependencies: 21 | prisma: '*' 22 | peerDependenciesMeta: 23 | prisma: 24 | optional: true 25 | dependencies: 26 | prisma: 5.19.1 27 | dev: false 28 | 29 | /@prisma/debug@5.19.1: 30 | resolution: {integrity: sha512-lAG6A6QnG2AskAukIEucYJZxxcSqKsMK74ZFVfCTOM/7UiyJQi48v6TQ47d6qKG3LbMslqOvnTX25dj/qvclGg==} 31 | dev: false 32 | 33 | /@prisma/engines-version@5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3: 34 | resolution: {integrity: sha512-xR6rt+z5LnNqTP5BBc+8+ySgf4WNMimOKXRn6xfNRDSpHvbOEmd7+qAOmzCrddEc4Cp8nFC0txU14dstjH7FXA==} 35 | dev: false 36 | 37 | /@prisma/engines@5.19.1: 38 | resolution: {integrity: sha512-kR/PoxZDrfUmbbXqqb8SlBBgCjvGaJYMCOe189PEYzq9rKqitQ2fvT/VJ8PDSe8tTNxhc2KzsCfCAL+Iwm/7Cg==} 39 | requiresBuild: true 40 | dependencies: 41 | '@prisma/debug': 5.19.1 42 | '@prisma/engines-version': 5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3 43 | '@prisma/fetch-engine': 5.19.1 44 | '@prisma/get-platform': 5.19.1 45 | dev: false 46 | 47 | /@prisma/fetch-engine@5.19.1: 48 | resolution: {integrity: sha512-pCq74rtlOVJfn4pLmdJj+eI4P7w2dugOnnTXpRilP/6n5b2aZiA4ulJlE0ddCbTPkfHmOL9BfaRgA8o+1rfdHw==} 49 | dependencies: 50 | '@prisma/debug': 5.19.1 51 | '@prisma/engines-version': 5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3 52 | '@prisma/get-platform': 5.19.1 53 | dev: false 54 | 55 | /@prisma/get-platform@5.19.1: 56 | resolution: {integrity: sha512-sCeoJ+7yt0UjnR+AXZL7vXlg5eNxaFOwC23h0KvW1YIXUoa7+W2ZcAUhoEQBmJTW4GrFqCuZ8YSP0mkDa4k3Zg==} 57 | dependencies: 58 | '@prisma/debug': 5.19.1 59 | dev: false 60 | 61 | /accepts@1.3.8: 62 | resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} 63 | engines: {node: '>= 0.6'} 64 | dependencies: 65 | mime-types: 2.1.35 66 | negotiator: 0.6.3 67 | dev: false 68 | 69 | /array-flatten@1.1.1: 70 | resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} 71 | dev: false 72 | 73 | /body-parser@1.20.2: 74 | resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} 75 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 76 | dependencies: 77 | bytes: 3.1.2 78 | content-type: 1.0.5 79 | debug: 2.6.9 80 | depd: 2.0.0 81 | destroy: 1.2.0 82 | http-errors: 2.0.0 83 | iconv-lite: 0.4.24 84 | on-finished: 2.4.1 85 | qs: 6.11.0 86 | raw-body: 2.5.2 87 | type-is: 1.6.18 88 | unpipe: 1.0.0 89 | transitivePeerDependencies: 90 | - supports-color 91 | dev: false 92 | 93 | /bytes@3.1.2: 94 | resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} 95 | engines: {node: '>= 0.8'} 96 | dev: false 97 | 98 | /call-bind@1.0.7: 99 | resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} 100 | engines: {node: '>= 0.4'} 101 | dependencies: 102 | es-define-property: 1.0.0 103 | es-errors: 1.3.0 104 | function-bind: 1.1.2 105 | get-intrinsic: 1.2.4 106 | set-function-length: 1.2.2 107 | dev: false 108 | 109 | /content-disposition@0.5.4: 110 | resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} 111 | engines: {node: '>= 0.6'} 112 | dependencies: 113 | safe-buffer: 5.2.1 114 | dev: false 115 | 116 | /content-type@1.0.5: 117 | resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} 118 | engines: {node: '>= 0.6'} 119 | dev: false 120 | 121 | /cookie-signature@1.0.6: 122 | resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} 123 | dev: false 124 | 125 | /cookie@0.6.0: 126 | resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} 127 | engines: {node: '>= 0.6'} 128 | dev: false 129 | 130 | /debug@2.6.9: 131 | resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} 132 | peerDependencies: 133 | supports-color: '*' 134 | peerDependenciesMeta: 135 | supports-color: 136 | optional: true 137 | dependencies: 138 | ms: 2.0.0 139 | dev: false 140 | 141 | /define-data-property@1.1.4: 142 | resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} 143 | engines: {node: '>= 0.4'} 144 | dependencies: 145 | es-define-property: 1.0.0 146 | es-errors: 1.3.0 147 | gopd: 1.0.1 148 | dev: false 149 | 150 | /depd@2.0.0: 151 | resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} 152 | engines: {node: '>= 0.8'} 153 | dev: false 154 | 155 | /destroy@1.2.0: 156 | resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} 157 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 158 | dev: false 159 | 160 | /ee-first@1.1.1: 161 | resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} 162 | dev: false 163 | 164 | /encodeurl@1.0.2: 165 | resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} 166 | engines: {node: '>= 0.8'} 167 | dev: false 168 | 169 | /es-define-property@1.0.0: 170 | resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} 171 | engines: {node: '>= 0.4'} 172 | dependencies: 173 | get-intrinsic: 1.2.4 174 | dev: false 175 | 176 | /es-errors@1.3.0: 177 | resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 178 | engines: {node: '>= 0.4'} 179 | dev: false 180 | 181 | /escape-html@1.0.3: 182 | resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} 183 | dev: false 184 | 185 | /etag@1.8.1: 186 | resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} 187 | engines: {node: '>= 0.6'} 188 | dev: false 189 | 190 | /express@4.19.2: 191 | resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} 192 | engines: {node: '>= 0.10.0'} 193 | dependencies: 194 | accepts: 1.3.8 195 | array-flatten: 1.1.1 196 | body-parser: 1.20.2 197 | content-disposition: 0.5.4 198 | content-type: 1.0.5 199 | cookie: 0.6.0 200 | cookie-signature: 1.0.6 201 | debug: 2.6.9 202 | depd: 2.0.0 203 | encodeurl: 1.0.2 204 | escape-html: 1.0.3 205 | etag: 1.8.1 206 | finalhandler: 1.2.0 207 | fresh: 0.5.2 208 | http-errors: 2.0.0 209 | merge-descriptors: 1.0.1 210 | methods: 1.1.2 211 | on-finished: 2.4.1 212 | parseurl: 1.3.3 213 | path-to-regexp: 0.1.7 214 | proxy-addr: 2.0.7 215 | qs: 6.11.0 216 | range-parser: 1.2.1 217 | safe-buffer: 5.2.1 218 | send: 0.18.0 219 | serve-static: 1.15.0 220 | setprototypeof: 1.2.0 221 | statuses: 2.0.1 222 | type-is: 1.6.18 223 | utils-merge: 1.0.1 224 | vary: 1.1.2 225 | transitivePeerDependencies: 226 | - supports-color 227 | dev: false 228 | 229 | /finalhandler@1.2.0: 230 | resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} 231 | engines: {node: '>= 0.8'} 232 | dependencies: 233 | debug: 2.6.9 234 | encodeurl: 1.0.2 235 | escape-html: 1.0.3 236 | on-finished: 2.4.1 237 | parseurl: 1.3.3 238 | statuses: 2.0.1 239 | unpipe: 1.0.0 240 | transitivePeerDependencies: 241 | - supports-color 242 | dev: false 243 | 244 | /forwarded@0.2.0: 245 | resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} 246 | engines: {node: '>= 0.6'} 247 | dev: false 248 | 249 | /fresh@0.5.2: 250 | resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} 251 | engines: {node: '>= 0.6'} 252 | dev: false 253 | 254 | /fsevents@2.3.3: 255 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 256 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 257 | os: [darwin] 258 | requiresBuild: true 259 | dev: false 260 | optional: true 261 | 262 | /function-bind@1.1.2: 263 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 264 | dev: false 265 | 266 | /get-intrinsic@1.2.4: 267 | resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} 268 | engines: {node: '>= 0.4'} 269 | dependencies: 270 | es-errors: 1.3.0 271 | function-bind: 1.1.2 272 | has-proto: 1.0.3 273 | has-symbols: 1.0.3 274 | hasown: 2.0.2 275 | dev: false 276 | 277 | /gopd@1.0.1: 278 | resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} 279 | dependencies: 280 | get-intrinsic: 1.2.4 281 | dev: false 282 | 283 | /has-property-descriptors@1.0.2: 284 | resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} 285 | dependencies: 286 | es-define-property: 1.0.0 287 | dev: false 288 | 289 | /has-proto@1.0.3: 290 | resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} 291 | engines: {node: '>= 0.4'} 292 | dev: false 293 | 294 | /has-symbols@1.0.3: 295 | resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} 296 | engines: {node: '>= 0.4'} 297 | dev: false 298 | 299 | /hasown@2.0.2: 300 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 301 | engines: {node: '>= 0.4'} 302 | dependencies: 303 | function-bind: 1.1.2 304 | dev: false 305 | 306 | /http-errors@2.0.0: 307 | resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} 308 | engines: {node: '>= 0.8'} 309 | dependencies: 310 | depd: 2.0.0 311 | inherits: 2.0.4 312 | setprototypeof: 1.2.0 313 | statuses: 2.0.1 314 | toidentifier: 1.0.1 315 | dev: false 316 | 317 | /iconv-lite@0.4.24: 318 | resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} 319 | engines: {node: '>=0.10.0'} 320 | dependencies: 321 | safer-buffer: 2.1.2 322 | dev: false 323 | 324 | /inherits@2.0.4: 325 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 326 | dev: false 327 | 328 | /ipaddr.js@1.9.1: 329 | resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} 330 | engines: {node: '>= 0.10'} 331 | dev: false 332 | 333 | /media-typer@0.3.0: 334 | resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} 335 | engines: {node: '>= 0.6'} 336 | dev: false 337 | 338 | /merge-descriptors@1.0.1: 339 | resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} 340 | dev: false 341 | 342 | /methods@1.1.2: 343 | resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} 344 | engines: {node: '>= 0.6'} 345 | dev: false 346 | 347 | /mime-db@1.52.0: 348 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 349 | engines: {node: '>= 0.6'} 350 | dev: false 351 | 352 | /mime-types@2.1.35: 353 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 354 | engines: {node: '>= 0.6'} 355 | dependencies: 356 | mime-db: 1.52.0 357 | dev: false 358 | 359 | /mime@1.6.0: 360 | resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} 361 | engines: {node: '>=4'} 362 | hasBin: true 363 | dev: false 364 | 365 | /ms@2.0.0: 366 | resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} 367 | dev: false 368 | 369 | /ms@2.1.3: 370 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 371 | dev: false 372 | 373 | /negotiator@0.6.3: 374 | resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} 375 | engines: {node: '>= 0.6'} 376 | dev: false 377 | 378 | /object-inspect@1.13.2: 379 | resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} 380 | engines: {node: '>= 0.4'} 381 | dev: false 382 | 383 | /on-finished@2.4.1: 384 | resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} 385 | engines: {node: '>= 0.8'} 386 | dependencies: 387 | ee-first: 1.1.1 388 | dev: false 389 | 390 | /parseurl@1.3.3: 391 | resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} 392 | engines: {node: '>= 0.8'} 393 | dev: false 394 | 395 | /path-to-regexp@0.1.7: 396 | resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} 397 | dev: false 398 | 399 | /prisma@5.19.1: 400 | resolution: {integrity: sha512-c5K9MiDaa+VAAyh1OiYk76PXOme9s3E992D7kvvIOhCrNsBQfy2mP2QAQtX0WNj140IgG++12kwZpYB9iIydNQ==} 401 | engines: {node: '>=16.13'} 402 | hasBin: true 403 | requiresBuild: true 404 | dependencies: 405 | '@prisma/engines': 5.19.1 406 | optionalDependencies: 407 | fsevents: 2.3.3 408 | dev: false 409 | 410 | /proxy-addr@2.0.7: 411 | resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} 412 | engines: {node: '>= 0.10'} 413 | dependencies: 414 | forwarded: 0.2.0 415 | ipaddr.js: 1.9.1 416 | dev: false 417 | 418 | /qs@6.11.0: 419 | resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} 420 | engines: {node: '>=0.6'} 421 | dependencies: 422 | side-channel: 1.0.6 423 | dev: false 424 | 425 | /range-parser@1.2.1: 426 | resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} 427 | engines: {node: '>= 0.6'} 428 | dev: false 429 | 430 | /raw-body@2.5.2: 431 | resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} 432 | engines: {node: '>= 0.8'} 433 | dependencies: 434 | bytes: 3.1.2 435 | http-errors: 2.0.0 436 | iconv-lite: 0.4.24 437 | unpipe: 1.0.0 438 | dev: false 439 | 440 | /safe-buffer@5.2.1: 441 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 442 | dev: false 443 | 444 | /safer-buffer@2.1.2: 445 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 446 | dev: false 447 | 448 | /send@0.18.0: 449 | resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} 450 | engines: {node: '>= 0.8.0'} 451 | dependencies: 452 | debug: 2.6.9 453 | depd: 2.0.0 454 | destroy: 1.2.0 455 | encodeurl: 1.0.2 456 | escape-html: 1.0.3 457 | etag: 1.8.1 458 | fresh: 0.5.2 459 | http-errors: 2.0.0 460 | mime: 1.6.0 461 | ms: 2.1.3 462 | on-finished: 2.4.1 463 | range-parser: 1.2.1 464 | statuses: 2.0.1 465 | transitivePeerDependencies: 466 | - supports-color 467 | dev: false 468 | 469 | /serve-static@1.15.0: 470 | resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} 471 | engines: {node: '>= 0.8.0'} 472 | dependencies: 473 | encodeurl: 1.0.2 474 | escape-html: 1.0.3 475 | parseurl: 1.3.3 476 | send: 0.18.0 477 | transitivePeerDependencies: 478 | - supports-color 479 | dev: false 480 | 481 | /set-function-length@1.2.2: 482 | resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} 483 | engines: {node: '>= 0.4'} 484 | dependencies: 485 | define-data-property: 1.1.4 486 | es-errors: 1.3.0 487 | function-bind: 1.1.2 488 | get-intrinsic: 1.2.4 489 | gopd: 1.0.1 490 | has-property-descriptors: 1.0.2 491 | dev: false 492 | 493 | /setprototypeof@1.2.0: 494 | resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} 495 | dev: false 496 | 497 | /side-channel@1.0.6: 498 | resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} 499 | engines: {node: '>= 0.4'} 500 | dependencies: 501 | call-bind: 1.0.7 502 | es-errors: 1.3.0 503 | get-intrinsic: 1.2.4 504 | object-inspect: 1.13.2 505 | dev: false 506 | 507 | /statuses@2.0.1: 508 | resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} 509 | engines: {node: '>= 0.8'} 510 | dev: false 511 | 512 | /toidentifier@1.0.1: 513 | resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} 514 | engines: {node: '>=0.6'} 515 | dev: false 516 | 517 | /type-is@1.6.18: 518 | resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} 519 | engines: {node: '>= 0.6'} 520 | dependencies: 521 | media-typer: 0.3.0 522 | mime-types: 2.1.35 523 | dev: false 524 | 525 | /unpipe@1.0.0: 526 | resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} 527 | engines: {node: '>= 0.8'} 528 | dev: false 529 | 530 | /utils-merge@1.0.1: 531 | resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} 532 | engines: {node: '>= 0.4.0'} 533 | dev: false 534 | 535 | /vary@1.1.2: 536 | resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} 537 | engines: {node: '>= 0.8'} 538 | dev: false 539 | -------------------------------------------------------------------------------- /demos/servers/prisma/db.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UTDallasEPICS/Guides/9419fbc43367f73efe1d83e8bddd5f3197c0e236/demos/servers/prisma/db.db -------------------------------------------------------------------------------- /demos/servers/prisma/migrations/20240908145758_initial/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Todo" ( 3 | "id" TEXT NOT NULL PRIMARY KEY, 4 | "title" TEXT NOT NULL, 5 | "content" TEXT 6 | ); 7 | -------------------------------------------------------------------------------- /demos/servers/prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "sqlite" -------------------------------------------------------------------------------- /demos/servers/prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | generator client { 2 | provider = "prisma-client-js" 3 | } 4 | 5 | datasource db { 6 | provider = "sqlite" 7 | url = env("DATABASE_URL") 8 | } 9 | 10 | model Todo { 11 | id String @id @default(cuid()) 12 | title String 13 | content String? 14 | } 15 | -------------------------------------------------------------------------------- /demos/servers/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Todo App 10 | 30 | 31 | 32 | 33 |

Todo App

34 |
35 | 36 | 37 | 38 | 39 | 40 |
41 |
42 | 43 |
44 | 45 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /demos/vue-demo/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require('@rushstack/eslint-patch/modern-module-resolution') 3 | 4 | module.exports = { 5 | root: true, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/eslint-config-typescript', 10 | '@vue/eslint-config-prettier/skip-formatting' 11 | ], 12 | parserOptions: { 13 | ecmaVersion: 'latest' 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /demos/vue-demo/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | 30 | *.tsbuildinfo 31 | -------------------------------------------------------------------------------- /demos/vue-demo/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "semi": false, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "printWidth": 100, 7 | "trailingComma": "none" 8 | } -------------------------------------------------------------------------------- /demos/vue-demo/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "Vue.volar", 4 | "dbaeumer.vscode-eslint", 5 | "esbenp.prettier-vscode" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /demos/vue-demo/README.md: -------------------------------------------------------------------------------- 1 | # vue-demo 2 | 3 | This template should help get you started developing with Vue 3 in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). 8 | 9 | ## Type Support for `.vue` Imports in TS 10 | 11 | TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types. 12 | 13 | ## Customize configuration 14 | 15 | See [Vite Configuration Reference](https://vitejs.dev/config/). 16 | 17 | ## Project Setup 18 | 19 | ```sh 20 | pnpm install 21 | ``` 22 | 23 | ### Compile and Hot-Reload for Development 24 | 25 | ```sh 26 | pnpm dev 27 | ``` 28 | 29 | ### Type-Check, Compile and Minify for Production 30 | 31 | ```sh 32 | pnpm build 33 | ``` 34 | 35 | ### Run Unit Tests with [Vitest](https://vitest.dev/) 36 | 37 | ```sh 38 | pnpm test:unit 39 | ``` 40 | 41 | ### Lint with [ESLint](https://eslint.org/) 42 | 43 | ```sh 44 | pnpm lint 45 | ``` 46 | -------------------------------------------------------------------------------- /demos/vue-demo/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /demos/vue-demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /demos/vue-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-demo", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "run-p type-check \"build-only {@}\" --", 9 | "preview": "vite preview", 10 | "test:unit": "vitest", 11 | "build-only": "vite build", 12 | "type-check": "vue-tsc --build --force", 13 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", 14 | "format": "prettier --write src/" 15 | }, 16 | "dependencies": { 17 | "pinia": "^2.1.7", 18 | "vue": "^3.4.29", 19 | "vue-router": "^4.3.3" 20 | }, 21 | "devDependencies": { 22 | "@rushstack/eslint-patch": "^1.8.0", 23 | "@tsconfig/node20": "^20.1.4", 24 | "@types/jsdom": "^21.1.7", 25 | "@types/node": "^20.14.5", 26 | "@vitejs/plugin-vue": "^5.0.5", 27 | "@vue/eslint-config-prettier": "^9.0.0", 28 | "@vue/eslint-config-typescript": "^13.0.0", 29 | "@vue/test-utils": "^2.4.6", 30 | "@vue/tsconfig": "^0.5.1", 31 | "eslint": "^8.57.0", 32 | "eslint-plugin-vue": "^9.23.0", 33 | "jsdom": "^24.1.0", 34 | "npm-run-all2": "^6.2.0", 35 | "prettier": "^3.2.5", 36 | "typescript": "~5.4.0", 37 | "vite": "^5.3.1", 38 | "vitest": "^1.6.0", 39 | "vue-tsc": "^2.0.21" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /demos/vue-demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UTDallasEPICS/Guides/9419fbc43367f73efe1d83e8bddd5f3197c0e236/demos/vue-demo/public/favicon.ico -------------------------------------------------------------------------------- /demos/vue-demo/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | 21 | 85 | -------------------------------------------------------------------------------- /demos/vue-demo/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demos/vue-demo/src/components/TodoItem.vue: -------------------------------------------------------------------------------- 1 | 11 | 20 | 87 | -------------------------------------------------------------------------------- /demos/vue-demo/src/components/__tests__/HelloWorld.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest' 2 | 3 | import { mount } from '@vue/test-utils' 4 | import HelloWorld from '../HelloWorld.vue' 5 | 6 | describe('HelloWorld', () => { 7 | it('renders properly', () => { 8 | const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } }) 9 | expect(wrapper.text()).toContain('Hello Vitest') 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /demos/vue-demo/src/components/icons/IconCommunity.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /demos/vue-demo/src/components/icons/IconDocumentation.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /demos/vue-demo/src/components/icons/IconEcosystem.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /demos/vue-demo/src/components/icons/IconSupport.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /demos/vue-demo/src/components/icons/IconTooling.vue: -------------------------------------------------------------------------------- 1 | 2 | 20 | -------------------------------------------------------------------------------- /demos/vue-demo/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createPinia } from 'pinia' 3 | 4 | import App from './App.vue' 5 | import router from './router' 6 | 7 | const app = createApp(App) 8 | 9 | app.use(createPinia()) 10 | app.use(router) 11 | 12 | app.mount('#app') 13 | -------------------------------------------------------------------------------- /demos/vue-demo/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | import Todo from '../views/Todo.vue' 3 | import Todos from '../views/Todos.vue' 4 | 5 | const router = createRouter({ 6 | history: createWebHistory(import.meta.env.BASE_URL), 7 | routes: [ 8 | { 9 | path: '/todos', 10 | name: 'home', 11 | component: Todos 12 | }, 13 | { 14 | path: '/todo/:todo', 15 | name: 'todo', 16 | component: Todo 17 | } 18 | ] 19 | }) 20 | 21 | export default router 22 | -------------------------------------------------------------------------------- /demos/vue-demo/src/stores/todos.ts: -------------------------------------------------------------------------------- 1 | import { ref, computed } from 'vue' 2 | import { defineStore } from 'pinia' 3 | 4 | export const useTodosStore = defineStore('todos', () => { 5 | const todos = ref([]) 6 | // TODO: thoughts on doing a full list reload vs client side update only 7 | const getTodos = async () => { 8 | const response = await fetch('/api/todos'); 9 | todos.value = await response.json(); 10 | } 11 | const createTodo = async (newTodo) => { 12 | await fetch('/api/todos', { 13 | method: 'POST', 14 | headers: { 15 | 'Content-Type': 'application/json' 16 | }, 17 | body: JSON.stringify(newTodo) 18 | }); 19 | await getTodos(); 20 | } 21 | 22 | const updateTodo = async (updatedTodo) => { 23 | await fetch(`/api/todos/${updatedTodo.id}`, { 24 | method: 'PUT', 25 | headers: { 26 | 'Content-Type': 'application/json' 27 | }, 28 | body: JSON.stringify(updatedTodo) 29 | }); 30 | await getTodos(); 31 | } 32 | 33 | const deleteTodo = async (id) => { 34 | await fetch(`/api/todos/${id}`, { 35 | method: 'DELETE' 36 | }); 37 | await getTodos(); 38 | } 39 | // TODO: thoughts on loading direct from the list vs loading from api 40 | // list may not have all values needed for editing due to performance considerations 41 | const getTodo = async id => { 42 | const res = await fetch(`/api/todo/${id}`, { 43 | method: 'GET' 44 | }); 45 | return await res.json() 46 | } 47 | 48 | return { todos, updateTodo, createTodo, deleteTodo, getTodos, getTodo } 49 | }) 50 | -------------------------------------------------------------------------------- /demos/vue-demo/src/views/Todo.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 32 | -------------------------------------------------------------------------------- /demos/vue-demo/src/views/Todos.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 21 | 22 | -------------------------------------------------------------------------------- /demos/vue-demo/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 8 | 9 | "baseUrl": ".", 10 | "paths": { 11 | "@/*": ["./src/*"] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /demos/vue-demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.node.json" 6 | }, 7 | { 8 | "path": "./tsconfig.app.json" 9 | }, 10 | { 11 | "path": "./tsconfig.vitest.json" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /demos/vue-demo/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node20/tsconfig.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "nightwatch.conf.*", 8 | "playwright.config.*" 9 | ], 10 | "compilerOptions": { 11 | "composite": true, 12 | "noEmit": true, 13 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 14 | 15 | "module": "ESNext", 16 | "moduleResolution": "Bundler", 17 | "types": ["node"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /demos/vue-demo/tsconfig.vitest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.app.json", 3 | "exclude": [], 4 | "compilerOptions": { 5 | "composite": true, 6 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", 7 | 8 | "lib": [], 9 | "types": ["node", "jsdom"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /demos/vue-demo/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [ 9 | vue(), 10 | ], 11 | resolve: { 12 | alias: { 13 | '@': fileURLToPath(new URL('./src', import.meta.url)) 14 | } 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /demos/vue-demo/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url' 2 | import { mergeConfig, defineConfig, configDefaults } from 'vitest/config' 3 | import viteConfig from './vite.config' 4 | 5 | export default mergeConfig( 6 | viteConfig, 7 | defineConfig({ 8 | test: { 9 | environment: 'jsdom', 10 | exclude: [...configDefaults.exclude, 'e2e/**'], 11 | root: fileURLToPath(new URL('./', import.meta.url)) 12 | } 13 | }) 14 | ) 15 | -------------------------------------------------------------------------------- /end_of_sem.md: -------------------------------------------------------------------------------- 1 | # End of Semester Requirements 2 | 3 | The following checklists are what is required for your code to be considered acceptable at the end of the semester. The general purpose is to ensure that someone can run your code without encountering any errors and everything the next team needs will be easy to find. ***If your project cannot be run based on the information and files on GitHub, your final project grade will be greatly reduced and/or your team will receive an incomplete until the information needed to run your project is provided!*** 4 | 5 | One potential way to make it easy to track all of these tasks is to create issues for each of the required tasks ([see this page about GitHub issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/quickstart#adding-a-task-list)). The first item in the [Project Structure/Files](#project-structurefiles) section would look something like this to automatically check the box when issue 3 is closed: 6 | 7 | ```markdown 8 | - [ ] #3 Database Files 9 | ``` 10 | 11 | ## Project Structure/Files 12 | 13 | These requirements must be met (in the main branch of your project) by the time your final report is due. 14 | 15 | - [ ] The most up to date version of all frontend and backend code 16 | - [ ] Database Files 17 | - [ ] If using docker: `docker-compose.yml` at top level for project database. 18 | - [ ] Database Schema 19 | - If using Prisma: `schema.prisma` in a top level `prisma` folder 20 | - If using other database: A file with a descriptive name used to create your database schema (e.g. `schema.sql`) 21 | - [ ] `.env.example` file containing NON-SENSITIVE environment variables (Auth0 Issuer, dev database url, etc). Sensitive environment variables should be replaced with example data such as `CLIENT_SECRET='EXAMPLE_CLIENT_SECRET'` so that future groups know that the environment variable is required. 22 | - [ ] Figma design files in a top level folder named `figma`. 23 | - [ ] Any migration scripts, dev scripts, etc. (if any exist) belong in a top level `scripts` folder. If the project has separate frontend/backend folders, the `scripts` folder should be in the backend folder. 24 | - [ ] Any other important files should be included in an appropriately named folder (such as `docs`, `documentation`, `notes`, etc.). 25 | 26 | ## Documentation 27 | 28 | A README.md file at the top level containing the following: 29 | 30 | - [ ] Conceptual overview - what is the project intended to accomplish? 31 | - Include a broad description of the different types of users/roles and what they do. 32 | - [ ] Functional requirements (broken down by page) - what are the discrete operations the app needs to be capable of? 33 | - [ ] Third party integrations and what they do in this project - HubSpot, Stripe, Auth0, etc. 34 | - [ ] Tech Stack 35 | - Include frontend framework (React, Vue, Svelte, etc.) 36 | - Include backend framework (Express.js, etc.) 37 | - If you use a meta framework, where the frontend and backend are combined, then you do not need to differentiate between frontend and backend (Next, Nuxt, Sveltekit, etc.) 38 | - Database (PostgreSQL, MySQL, MongoDB, etc.) 39 | - Other important packages (UI plugins, database connectors like prisma) 40 | - Other tools used/needed (such as Postman) 41 | - [ ] Deployment notes (if project is currently or in the process of being deployed) - is the partner running the application on their own servers or are they using something like AWS or Azure? 42 | - [ ] Migration scripts - do we need to import any data from an existing system that the partner is using? 43 | - [ ] ***Instructions for setting up the development environment!!!*** Assume that the needed software is already installed (Node.js, Docker, etc.). 44 | - How do you start your project? 45 | - How do you initialize the database? 46 | - How do you set up authentication? 47 | - Etc. 48 | 49 | ### Additional Documentation 50 | 51 | In the repo's GitHub wiki, include the following: 52 | 53 | - [ ] List of user workflows (each different type of user) 54 | - [ ] Each workflow should have a corresponding wiki page, linked in the list, that either contains the workflow information or a TODO. 55 | - [ ] Each workflow should list the pages involved 56 | - [ ] List of user roles and what each role is able to do 57 | - [ ] Every third party integration should have its own page describing what parts of that service are used, how, and why 58 | 59 | These should be high level - you should not be explaining every line of code. 60 | 61 | ## Examples 62 | See [Comet Cupboard](https://github.com/UTDallasEPICS/Comet-Cupboard/wiki#shopping-functionality) and [Wellness Center](https://github.com/UTDallasEPICS/Wellness-Center-Older-Adults/wiki) for examples of what documentation can look like. 63 | -------------------------------------------------------------------------------- /languages_and_frameworks/express.md: -------------------------------------------------------------------------------- 1 | # Node.js Express Server 2 | 3 | ## [Express Routes and Controllers](https://developer.mozilla.org/en-US/docs/Learn/Server-side/*Express_Nodejs/routes) 4 | 5 | ![Express Model](https://i.imgur.com/0brzKNe.png) 6 | ![Middleware Diagram](https://i.imgur.com/VZdyPL5.png) 7 | 8 | * [Model View Controller (MVC) Design](https://stackoverflow.com/questions/11066958/in-the-model-view-controller-principle-what-is-the-frontend-and-what-is-the-bac) 9 | * [Controller vs Middleware](https://stackoverflow.com/questions/57274465/whats-the-difference-between-a-controller-and-a-middleware) 10 | * [Express Routing Guide](https://expressjs.com/en/guide/routing.html) 11 | 12 | * Routing refers to how an application’s endpoints (URIs) respond to client requests 13 | * forwards the supported requests to appropriate controller functions 14 | * Middleware is code that examines an incoming request and prepares it for further processing 15 | * Controller functions to get the requested data from the models, create an HTML page displaying the data, and return it to the user to view in the browser 16 | * Views used by the controllers to render the data. 17 | * [URI vs URL](https://danielmiessler.com/study/difference-between-uri-url/) 18 | * [Endpoints](https://developer.wordpress.org/rest-api/extending-the-rest-api/routes-and-endpoints/#overview) 19 | * Endpoints are functions available through the API 20 | * the destination that a route needs to map to 21 | * like retrieving the API index, updating a post, or deleting a comment 22 | * GET, POST, DELETE 23 | 24 | ## Express Server General Properties 25 | 26 | * Has Rest APIs 27 | * Login and Registration 28 | * Express routes 29 | * supports JWT (JSONWebToken) 30 | * access is verified by JWT Token in HttpOnly Cookies 31 | * TODO auth0 32 | * works with MongoDB database 33 | * uses Mongoose ODM (Object Data Modeling) library for MongoDB and Node. js 34 | * Role based Authorization 35 | * employee users can sign up, sign in 36 | * public page to sign in 37 | 38 | ## server.js 39 | 40 | * [import vs require](https://www.geeksforgeeks.org/difference-between-node-js-require-and-es6-import-and-export/#:~:text=Require%20is%20Non%2Dlexical%2C%20it,the%20beginning%20of%20the%20file.) 41 | * [Cross-origin Requests (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) 42 | * [CORS in 100 Seconds](https://www.youtube.com/watch?v=4KHiSt0oLJ0) 43 | * [CORS in Express](https://www.section.io/engineering-education/how-to-use-cors-in-nodejs-with-express/) 44 | * mechanism to allow website on 1 URL to request data from another different URL 45 | * Browser implements a Same-Origin Policy for security 46 | * allows a website to freely request data from its own URL but blocks anything from an external URL unless certain conditions are met 47 | * Requests has an Origin header to request message 48 | * browser allows request to server on same origin 49 | * if request goes to different URL - is a cross-origin request 50 | * server will add Access-Control-Allow-Origin header to response 51 | * its value needs to match the origin header 52 | * Express CORS middleware to respond with the proper CORS header on every response 53 | * [express.json and express.urlencoded](https://stackoverflow.com/questions/23259168/what-are-express-json-and-express-urlencoded) 54 | * DO NOT NEED `express.json()` and `express.urlencoded()` for GET Requests or DELETE Requests 55 | * For POST and PUT requests, because in both these requests you are sending data (in the form of some data object) to the server and you are asking the server to accept or store that data (object), which is enclosed in the body (i.e. req.body) of that (POST or PUT) Request 56 | * `bodyParser.json` 57 | * returns middleware that only parses incoming JSON requests and puts the parsed data in req.body 58 | * recognize the incoming Request Object as a JSON Object 59 | * POST requests 60 | * `bodyParser.urlencoded({extended: ...})` 61 | * parses incoming requests with urlencoded payloads 62 | * recognize the incoming Request Object as strings or arrays (or nested objects with true) 63 | * tells the system whether you want to use a simple algorithm for shallow parsing (i.e. false) or complex algorithm (i.e. true) for deep parsing that can deal with nested objects 64 | * PUT requests 65 | * Environment Variables 66 | * Variables with values 67 | * for setting configuration options as well as storing important values securely 68 | * [Dotenv](https://www.npmjs.com/package/dotenv) 69 | * npm package to allow developers to create a .env file that has custom environment files that are added into the process.env object 70 | * [Why i should not use dotenv in production mode](https://stackoverflow.com/questions/67604414/why-i-should-not-use-dotenv-in-production-mode) 71 | * [DEV vs PROD](https://nodejs.dev/en/learn/nodejs-the-difference-between-development-and-production/) 72 | * [How to use Environment Variables in NodeJs with Express and Dotenv](https://www.mickpatterson.com.au/blog/how-to-use-environment-variables-in-nodejs-with-express-and-dotenv) 73 | -------------------------------------------------------------------------------- /languages_and_frameworks/webdev_pathway.md: -------------------------------------------------------------------------------- 1 | # Web Development Pathway 2 | 3 | Some of these tutorials may use Sublime, or Atom - it is STRONGLY recommended that you use vscode. 4 | Some of these may use MongoDB. DO NOT USE MONGODB. Instead, use Prisma and either PostgreSQL or MySQL. 5 | Some of these use React/Next. We STRONGLY reccommend Vue/Nuxt over React/Next. 6 | 7 | Breaking out of just reading tutorials 8 | https://www.codewell.cc/blog/how-to-escape-tutorial-hell-and-start-building-your-own-projects 9 | 10 | The Odin Project 11 | https://www.theodinproject.com/paths/full-stack-javascript 12 | 13 | CSS for absolute beginners 14 | https://www.youtube.com/watch?v=yfoY53QXEnI 15 | 16 | Visual guide to CSS Selectors 17 | https://fffuel.co/css-selectors/ 18 | 19 | JS Crash Course 20 | https://www.youtube.com/watch?v=hdI2bqOjy3c 21 | 22 | CSS Grid 23 | https://scrimba.com/learn/cssgrid 24 | 25 | CSS Flexbox 26 | https://www.youtube.com/playlist?list=PLC3y8-rFHvwg6rjbiMadCILrjh7QkvzoQ 27 | 28 | HTML for absolute beginners 29 | https://www.youtube.com/watch?v=UB1O30fR-EE 30 | 31 | Overview of the Chrome DevTools - note that every browser engine (Firefox, Chrome/Chromium, Safari) has it's own equivalent 32 | https://developer.chrome.com/docs/devtools/ 33 | 34 | NodeJS 35 | For the API section: Express 36 | Database section: Prisma + Postgresql 37 | Testing: Vitest 38 | https://roadmap.sh/nodejs 39 | 40 | Vue Roadmap 41 | https://roadmap.sh/vue 42 | https://vuejs.org 43 | 44 | JS Overview 45 | https://javascript.info 46 | 47 | JS Roadmap 48 | https://roadmap.sh/javascript 49 | 50 | # Creating a Webpage with CSS, HTML, and JS 51 | 52 | Prerequisites: 53 | 54 | - VSCode/VSCodium (or text editor of choice) 55 | - Familiar with CLI - navigating to folders, running commands 56 | - Python (should still come preinstalled on macOS, may need to upgrade to python 3) 57 | 58 | ## Serving a website/webapp 59 | 60 | Web content is delivered to the users browser from the server via HTTP/HTTPS. We can achieve this in our local dev environment in several ways. Here we look at directly opening a file and using pythons http.server module. 61 | 62 | ### Create an HTML file 63 | 64 | 1. Open a text editor and create a new file called index.html 65 | 2. Add the following HTML: 66 | 67 | ```html 68 | 69 | 70 | My Page 71 | 72 | 73 |

Hello World!

74 |

This is my web page.

75 | 76 | 77 | ``` 78 | 79 | 3. Save the file 80 | 81 | ### View in browser 82 | 83 | 1. Open your web browser (Chrome, Firefox etc) 84 | 2. Go to File > Open File 85 | 3. Select the index.html file you just created 86 | 4. The web page with "Hello World!" text will open 87 | 88 | #### Serve with Python 89 | 90 | 1. Open terminal/command prompt 91 | 2. Navigate to directory containing index.html 92 | 3. Run: 93 | `python -m http.server 8000` 94 | 95 | 4. Open browser and go to http://localhost:8000 96 | 5. You will see your index.html rendered 97 | 6. Press Ctrl+C in terminal to stop the server 98 | 99 | ## Creating and styling elements 100 | 101 | ### Add HTML elements 102 | 103 | In index.html: 104 | 105 | ```html 106 |
107 |

My Website

108 | 113 |
114 | 115 |
116 |
117 |

Card Title

118 |

This is some card content

119 |
120 | 121 |
122 |

Another Card

123 |

Some content for this card

124 |
125 |
126 | ``` 127 | 128 | ### CSS Tags 129 | 130 | CSS can go in two places. `` tags can contain class definitions. You can also write the rules for an element directly in the `style` attribute of any HTML element. 131 | 132 | ```html 133 | 141 | 142 |

143 |
144 |

My Website

145 | 150 |
151 | 152 |
153 |
154 |

Card Title

155 |

This is some card content

156 |
157 | 158 |
159 |

Another Card

160 |

Some content for this card

161 |
162 |
163 | ``` 164 | 165 | ### Grid Layout 166 | 167 | The grid layout allows elements to be organized in rows and columns: 168 | 169 | ```css 170 | .content { 171 | display: grid; 172 | grid-template-columns: 1fr 1fr; 173 | } 174 | ``` 175 | 176 | ### Flexbox Layout 177 | 178 | Flexbox allows responsive alignment using flex properties: 179 | 180 | ```css 181 | .header { 182 | display: flex; 183 | align-items: center; 184 | } 185 | ``` 186 | 187 | This by default will render as a horizontal row. You can make it vertical with `flex-direction: column;` 188 | 189 | ### Block Layout 190 | 191 | This is the default display mode. 192 | 193 | Block displays elements stacked vertically, at full width: 194 | 195 | ```css 196 | .card { 197 | display: block; 198 | } 199 | ``` 200 | 201 | ### Styling and Transitions 202 | 203 | ```css 204 | .header { 205 | background: #eee; 206 | padding: 20px; 207 | } 208 | 209 | .card { 210 | background: white; 211 | border: 1px solid #ddd; 212 | padding: 20px; 213 | transition: box-shadow 0.3s; 214 | } 215 | 216 | .card:hover { 217 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); 218 | } 219 | ``` 220 | 221 | ## Brief discussion of JS 222 | 223 | There will be words here that you are not familiar with - look them up or ask! 224 | 225 | ### Vanilla JS 226 | 227 | - JS was created in 1995 to add dynamic interactivity to web pages 228 | - Early JS was simple scripts that manipulated the DOM (Document Object Model) 229 | - Issues with loading complex projects due to async fetching 230 | - Kinda hacky/janky, but in a lovable way 231 | 232 | ### jQuery 233 | 234 | - jQuery was released in 2006 and quickly became popular 235 | - It simplified DOM manipulation, AJAX (network calls), events etc with easy-to-use methods 236 | - Still no guarantee an import will exist by the time it needs to be executed 237 | 238 | ### JS Frameworks 239 | 240 | - AngularJS (2010) and other frameworks emerged to manage code complexity 241 | - React (2013) and Vue (2014) provided component architectures 242 | - Allowed building complex SPAs efficiently 243 | - Two-way data binding 244 | - Markup languages other than HTML such as Pug and JSX 245 | 246 | ### Bundlers like Webpack/Rollup 247 | 248 | - As apps grew, bundling tools like Webpack were needed 249 | - They bundle JS modules and assets into optimized files 250 | - Enables features like code splitting for efficient delivery 251 | - Solve the issues of async asset loading in browsers 252 | 253 | ### Meta Frameworks 254 | 255 | - Next.js (2016) and Nuxt (2017) built on React and Vue 256 | - Provide server-side rendering, routing, optimizations 257 | - Complete framework for building web applications 258 | - Deeper integration between frontend and backend code 259 | 260 | The progression has been towards more powerful abstractions to manage complexity and enable faster development. 261 | 262 | ### JSON (JS Object Notation) 263 | 264 | - JSON is a text-based format for representing structured data 265 | - Based on JS object syntax, available for all major languages 266 | - JSON uses key-value pairs, arrays, and objects to store data 267 | - Keys and strings are enclosed in double quotes 268 | - JSON is very machine AND human readable 269 | - JSON is lightweight and more compact than alternatives like XML 270 | - JSON data is transmitted as plain text, MIME type application/json 271 | - Can be converted to/from native JS objects 272 | 273 | ### Node.js 274 | 275 | - Node.js allows running JS on the server 276 | - Having one language across the full stack simplifies a lot of things 277 | 278 | ```js 279 | // Load HTTP module 280 | const http = require("http"); 281 | 282 | // Create server 283 | const server = http.createServer((req, res) => { 284 | // Handle request 285 | }); 286 | 287 | // Listen on port 288 | server.listen(3000); 289 | ``` 290 | 291 | ### Express 292 | 293 | - Express is a web framework for Node.js 294 | - Makes it easier to build web apps and APIs 295 | - H3 is a more modern HTTP framework 296 | 297 | ```js 298 | // Load express 299 | const express = require("express"); 300 | 301 | // Create app 302 | const app = express(); 303 | 304 | // Handle GET request 305 | app.get("/api/items", (req, res) => { 306 | // Return JSON response 307 | res.json([{ id: 1 }, { id: 2 }]); 308 | }); 309 | 310 | // Listen on port 311 | app.listen(3000); 312 | ``` 313 | 314 | ### Async/Await & Promises 315 | 316 | The Promise API and async/await are both features in JavaScript that help handle asynchronous operations. They are used to write cleaner and more readable code when dealing with asynchronous tasks like making network requests or handling database operations. 317 | 318 | The Promise API is a way to handle asynchronous operations using a chain of methods, such as .then() and .catch(). Here's an example: 319 | 320 | ```js 321 | fetch("https://api.example.com/data") 322 | .then(response => response.json()) 323 | .then(data => console.log(data)) 324 | .catch(error => console.log(error)); 325 | ``` 326 | 327 | In this example, we use the fetch() function to make a network request to retrieve data from an API. The .then() method is used to handle the response and extract the JSON data. The second .then() method is used to log the data to the console. If an error occurs during the request, the .catch() method is used to handle and log the error. 328 | 329 | On the other hand, async/await is a more modern approach to handle asynchronous operations in JavaScript. It allows you to write asynchronous code that looks and behaves more like synchronous code. Here's an example: 330 | 331 | ```js 332 | async function fetchData() { 333 | try { 334 | const response = await fetch("https://api.example.com/data"); 335 | const data = await response.json(); 336 | console.log(data); 337 | } catch (error) { 338 | console.log(error); 339 | } 340 | } 341 | 342 | fetchData(); 343 | ``` 344 | 345 | In this example, we define an async function called fetchData() that uses the await keyword to pause the execution until the promise is resolved or rejected. The try block is used to wrap the code that may throw an error, and the catch block is used to handle any errors that occur during the execution. 346 | 347 | Comparing the two approaches, async/await offers a more concise and readable way to write asynchronous code. It eliminates the need for a long chain of .then() methods, making the code easier to understand and maintain. Additionally, async/await allows you to handle errors using a more familiar try-catch syntax, which makes error handling more straightforward. 348 | 349 | However, it's important to note that async/await is built on top of the Promise API. Under the hood, async/await still uses promises, so they are not completely different concepts but rather a different way of using promises. 350 | 351 | Also note that you can only use the await keyword inside of an async function, although there are some special cases where await can be used at the top level of a file (i.e. not inside of an async function). 352 | 353 | ### REST APIs 354 | 355 | - REST APIs provide data via HTTP endpoints 356 | - Express allows building REST APIs simply 357 | - Route parameters - extract parts of an URL to use as variables 358 | - Query parameters - https://example.com/?queryParam1=value&queryParam2=5 359 | - Body - GET requests by convention do not allow a body, must use route/query parameters 360 | 361 | ``` 362 | GET /api/items - get items 363 | POST /api/item - create item 364 | PUT /api/item/1 - update item with id 1 365 | DELETE /api/item/1 - delete item with id 1 366 | ``` 367 | 368 | ### Network calls using fetch 369 | 370 | Making a GET request: 371 | 372 | ```js 373 | async function getMovies() { 374 | const response = await fetch("http://example.com/movies.json"); 375 | const data = await response.json(); // this reads the body of the response into a JSON object 376 | 377 | console.log(data); 378 | } 379 | 380 | getMovies(); 381 | ``` 382 | 383 | Note that with GET requests, you _should not_ provide a request body. Pass information along in the query parameters, which the server can extract and use. 384 | 385 | ````js 386 | async function getMovies() { 387 | 388 | // Add query parameters 389 | const queryParams = '?limit=10&genre=action'; 390 | 391 | const response = await fetch('http://example.com/movies.json' + queryParams); 392 | 393 | const data = await response.json(); 394 | 395 | console.log(data); 396 | 397 | } 398 | 399 | getMovies();``` 400 | 401 | Making a POST request: 402 | ``` js 403 | async function addMovie() { 404 | const response = await fetch('http://example.com/movies', { 405 | method: 'POST', 406 | body: JSON.stringify({title: 'Star Wars'}) // note that the body MUST be a string 407 | }); 408 | 409 | const data = await response.json(); 410 | 411 | console.log(data); 412 | } 413 | 414 | addMovie(); 415 | ```` 416 | 417 | DELETE and UPDATE requests work in the same way as POST. 418 | 419 | ## Use JS to interact with users 420 | 421 | ### Add button and text input 422 | 423 | In HTML: 424 | 425 | ```html 426 | 427 | 428 | 429 | 430 |

Counter: 0

431 | 432 |

Name:

433 | ``` 434 | 435 | ### JS 436 | 437 | JS will always go inside of a `` tag. It can be loaded over network or be written directly inside the tag. 438 | 439 | `` will load a specific file over network. 440 | 441 | An example inside a script tag: 442 | 443 | ```html 444 | 447 | ``` 448 | 449 | Add the following to the above HTML: 450 | 451 | ```js 452 | // Get elements 453 | /* these are javascript objects that represent elements in the page itself */ 454 | const incrementBtn = document.getElementById("increment"); 455 | const nameInput = document.getElementById("nameInput"); 456 | const counterEl = document.getElementById("counter"); 457 | const nameEl = document.getElementById("name"); 458 | 459 | // Increment counter on click 460 | let counter = 0; 461 | /* this will run the function when the element emits the 'click' event - elements have a bunch of events they can emit */ 462 | incrementBtn.addEventListener("click", () => { 463 | // this is an arrow function 464 | counter++; 465 | counterEl.textContent = counter; 466 | }); 467 | 468 | // Update name on input change 469 | nameInput.addEventListener("input", () => { 470 | nameEl.textContent = nameInput.value; 471 | }); 472 | ``` 473 | 474 | # Creating a project with a JS framework 475 | 476 | ## A simple Single Page Application with Vue 477 | 478 | ### Event listeners vs Two way data binding 479 | 480 | Vanilla JavaScript Event Listeners 481 | 482 | - Add event listeners directly to DOM elements to listen for events like click, change, keyup etc. 483 | - Need to manually update state and re-render UI on each event. 484 | - Example: 485 | 486 | ```js 487 | // Get input element 488 | const input = document.getElementById("input"); 489 | 490 | // Add change event listener 491 | input.addEventListener("change", event => { 492 | // Manually update state 493 | state.inputValue = event.target.value; 494 | 495 | // Re-render UI with new state 496 | renderInput(); 497 | }); 498 | 499 | function renderInput() { 500 | // Update input with state.inputValue 501 | } 502 | ``` 503 | 504 | Vue Two-Way Data Binding 505 | 506 | - Declaratively bind data properties to DOM with v-model directive. 507 | - Reactivity system automatically detects changes and re-renders. 508 | - Example: 509 | ````js 510 | // In Vue component 511 | data() { 512 | return { 513 | inputValue: '' 514 | } 515 | }``` 516 | ```` 517 | 518 | // Template 519 | 520 | 521 | - inputValue will automatically update and re-render when input changes without needing event listener. 522 | 523 | ### npm/pnpm/yarn and package management 524 | 525 | npm 526 | 527 | - Default package manager for Node.js 528 | - Stores all versions of packages in node_modules folder 529 | - Large storage footprint due to storing multiple copies of packages 530 | - package.json lists project dependencies and metadata 531 | - Run npm install to install dependencies from package.json 532 | yarn 533 | - Alternative package manager developed by Facebook 534 | - Uses same package.json structure as npm 535 | - Installs packages faster than npm in some cases 536 | - More robust checksum security than npm 537 | - More emojis! 538 | pnpm 539 | - Alternative package manager focused on efficiency 540 | - Uses a content-addressable storage model to avoid duplicate packages 541 | - Links duplicated packages together via hard links 542 | - Smaller storage footprint than npm 543 | - Faster installs than npm 544 | - Supports npm packages and package.json file 545 | Package.json: 546 | - Defines app name, version, description 547 | - Lists project dependencies and versions 548 | - Can define scripts, config etc 549 | - Required for any Node.js project using a package manager 550 | - Running npm install or yarn will install all dependencies listed in package.json 551 | 552 | ### Project setup 553 | 554 | TODO: need to go over project setup 555 | 556 | `npm install vue@next pug typescript` 557 | 558 | ### App.vue 559 | 560 | ```html 561 | 564 | 565 | 566 | ``` 567 | 568 | ### LearningExamples.vue 569 | 570 | ```html 571 | 581 | 582 | 586 | ``` 587 | 588 | You should be able to figure out how to use CSS to make the above examples look nice. 589 | 590 | ## Using a server & database 591 | 592 | ### Prisma 593 | 594 | Prisma is an open-source ORM for Node.js and TypeScript. It provides a type-safe database client auto-generated from a data model. It also has features for managing the database schema and handling migrations. 595 | 596 | - Declarative data modeling using schema.prisma 597 | - Auto-generated and type-safe database client 598 | - Query building and validation 599 | - Database migrations 600 | - Works with all major databases 601 | 602 | ### Express & H3 603 | 604 | Express and H3 are frameworks for setting up easy REST servers. They provide support for routing and utilities for reading data out of query parameters and request bodies, managing cookies, redirects, etc. They are both used in essentially the same way, but H3 provides a composable, functional interface vs the more OOP interface of Express. H3 is used by Nuxt under the hood as part of its file-based routing system. 605 | 606 | Both frameworks have extensive ecosystem support. H3 is aided by an easy interface for compatibility with Express oriented libraries. 607 | 608 | H3 and Express do have some provided database adapters, but we want to leverage Prisma's additional schema management features. 609 | 610 | ### Example Prisma schema 611 | 612 | Key points: 613 | 614 | - The datasource block defines a PostgreSQL database connection. 615 | - The generator block specifies we want a Prisma Client JS library generated. 616 | - User model has name, email, and a relation to Todo items. id is generated with cuid(). 617 | - Todo model has title, optional content, and a relation to User via the userId foreign key. 618 | - The @relation and @references directives establish the 1:M relationship between User and Todo. 619 | 620 | ```prisma 621 | datasource db { 622 | provider = "postgresql" 623 | url = env("DATABASE_URL") 624 | } 625 | 626 | generator client { 627 | provider = "prisma-client-js" 628 | } 629 | 630 | model User { 631 | id String @id @default(cuid()) 632 | name String 633 | email String @unique 634 | Todos Todo[] // one to many relationship: one user has many Todos 635 | } 636 | 637 | model Todo { 638 | id String @id @default(cuid()) 639 | title String 640 | content String? 641 | userId String 642 | User User @relation(fields: [userId], references: [id]) // other side, many to one relationship: one Todo belongs to one User 643 | } 644 | ``` 645 | 646 | Corresponding class diagram 647 | 648 | ```mermaid 649 | classDiagram 650 | 651 | class User { 652 | +id: String 653 | +name: String 654 | +email: String 655 | +todos: Todo[] 656 | } 657 | 658 | class Todo { 659 | +id: String 660 | +title: String 661 | +content: String 662 | +userId: String 663 | } 664 | 665 | User "1" --o "0..*" Todo : has 666 | Todo "0..*" --o "1" User : belongs to 667 | ``` 668 | 669 | ### Example Express implementation 670 | 671 | ```js 672 | // user routes elided for brevity 673 | const express = require("express"); 674 | const { PrismaClient } = require("@prisma/client"); 675 | 676 | const prisma = new PrismaClient(); 677 | const app = express(); 678 | 679 | app.use(express.json()); 680 | 681 | // Create todo 682 | app.post("/todos", async (req, res) => { 683 | const todo = await prisma.todo.create({ 684 | data: { 685 | title: req.body.title, 686 | content: req.body.content, 687 | user: { connect: { id: req.body.userId } }, 688 | }, 689 | }); 690 | res.json(todo); 691 | }); 692 | 693 | // Update todo 694 | app.put("/todos/:id", async (req, res) => { 695 | const todo = await prisma.todo.update({ 696 | where: { id: req.params.id }, 697 | data: req.body, 698 | }); 699 | res.json(todo); 700 | }); 701 | 702 | // Get all todos 703 | app.get("/todos", async (req, res) => { 704 | const todos = await prisma.todo.findMany(); 705 | res.json(todos); 706 | }); 707 | 708 | // Get single todo 709 | app.get("/todos/:id", async (req, res) => { 710 | const todo = await prisma.todo.findUnique({ 711 | where: { id: req.params.id }, 712 | }); 713 | res.json(todo); 714 | }); 715 | 716 | app.listen(3000, () => { 717 | console.log("Server started on http://localhost:3000"); 718 | }); 719 | ``` 720 | 721 | ### Example H3 implementation 722 | 723 | ```ts 724 | // user routes elided for brevity 725 | import { createApp, eventHandler, toNodeListener } from "h3"; 726 | import { readBody } from "@h3/composables"; 727 | import { PrismaClient } from "@prisma/client"; 728 | import { createServer } from "node:http"; 729 | const prisma = new PrismaClient(); 730 | const app = createApp(); 731 | // Todo Routes 732 | 733 | app.post( 734 | "/todos", 735 | eventHandler(async event => { 736 | const body = await readBody(event); 737 | 738 | return prisma.todo.create({ 739 | data: { 740 | title: body.title, 741 | content: body.content, 742 | user: { connect: { id: body.userId } }, 743 | }, 744 | }); 745 | }) 746 | ); 747 | 748 | app.get( 749 | "/todos", 750 | eventHandler(async () => { 751 | return prisma.todo.findMany(); 752 | }) 753 | ); 754 | 755 | app.get( 756 | "/todos/:id", 757 | eventHandler(async event => { 758 | return prisma.todo.findUnique({ 759 | where: { id: event.params.id }, 760 | }); 761 | }) 762 | ); 763 | 764 | app.put( 765 | "/todos/:id", 766 | eventHandler(async event => { 767 | const body = await readBody(event); 768 | 769 | return prisma.todo.update({ 770 | where: { id: event.params.id }, 771 | data: body, 772 | }); 773 | }) 774 | ); 775 | 776 | app.delete( 777 | "/todos/:id", 778 | eventHandler(async event => { 779 | return prisma.todo.delete({ 780 | where: { id: event.params.id }, 781 | }); 782 | }) 783 | ); 784 | createServer(toNodeListener(app)).listen(3000); 785 | ``` 786 | 787 | # Metaframeworks 788 | 789 | Metaframeworks (Next, Nuxt, SvelteKit, etc) combine a frontend framework and a backend framework in one overarching metaframework. Instead of managing a separate API and frontend, they live in the same codebase. This makes several things much more straightforward - you don't have to juggle types or utility functions that are used in both, you can do neat stuff with SSR/SSG, and some common boilerplate gets removed. For example, in Nuxt you can write an API call such that when a page using that API call is loaded, the server makes the API call before sending the page to the client - this means that the client doesn't have to wait for the page to load and then make the API call, so the user gets a better experience. 790 | 791 | # Databases 792 | 793 | A database is a structured collection of data stored in a computer system. Databases allow you to efficiently store, organize, and query large amounts of data. 794 | 795 | The data in a database is organized into tables, with rows representing individual records or items, and columns representing the attributes of each item. For example, a table storing customer data might have columns for name, address, phone number, etc. 796 | To retrieve or manipulate data in a database, you use a query language like SQL (Structured Query Language). SQL allows you to write queries to select specific data, filter rows, join tables, update records, and more. Non-technical users can interact with a database through a custom frontend application. 797 | 798 | There are two main types of database models: 799 | 800 | - SQL databases are relational databases, where data is structured in relations (tables) and data has relationships between tables. SQL databases use SQL and are optimized for complex queries. Examples are MySQL, Oracle, SQL Server. 801 | - NoSQL databases have flexible, non-tabular data models. They are optimized for scalability and high performance on large volumes of data. But they sacrifice some functionality of relational SQL models. Examples are MongoDB, Cassandra, Redis. 802 | 803 | SQL databases are better for complex queries, relationships between data, and data consistency. NoSQL is better for simplicity of design, handling big data, and flexibility. Most real-world systems use a mix of SQL and NoSQL databases as needed. For example, a SQL database to store core business data, and a NoSQL cache layer to quickly serve common queries. 804 | 805 | Generally speaking you will end up with an SQL server for your core schema, and Redis, Memcached, or some other key-value store for your caching layer, if you even need one - in some cases you don't need a database cache, you can simply write to static files use a CDN like Cloudfront or Fastly. 806 | 807 | You will almost never really need to reach for something like MongoDB outside of specific use cases that you will _not_ encounter at the scale of an EPICS project or even most business applications. Remember that business people can usually speak SQL, and their reporting tooling always does. 808 | 809 | # Browser Dev Tools 810 | 811 | On most browsers, ctrl+shift+i, or right click on a webpage and look for 'Inspect'. Overview of the most important tabs below. 812 | 813 | Note that most frameworks have plugins for the dev tools that enable debugging of framework stuff. 814 | 815 | There are lots of useful buttons. See [here](https://developer.chrome.com/docs/devtools/). These look a little different in Firefox vs Safari vs Chromium (Brave, Edge, Chrome, Vivaldi, etc) 816 | 817 | ## Inspector 818 | 819 | - View and modify the DOM and CSS 820 | - Inspect element styles and layout 821 | - Debug accessibility issues 822 | - Change CSS styles dynamically (as in, change the appearance of a page without touching the actual source) 823 | 824 | ## Console 825 | 826 | - Log messages (console.log outputs here) and debug JS 827 | - Execute JS 828 | - Interact with page scripts 829 | - Inspect variable values 830 | - View network requests/errors 831 | 832 | ## Network 833 | 834 | - Monitor network requests and responses 835 | - Inspect request headers, params, response codes 836 | - Check load times and transfer sizes 837 | - Filter requests by type (XHR, JS, CSS, etc.) 838 | - View detailed timing breakdown 839 | -------------------------------------------------------------------------------- /npts_links: -------------------------------------------------------------------------------- 1 | EPICS 2024 summar plan: https://frill-tractor-b7a.notion.site/EPICS-2024-Summer-Plan-0e044dabd0d54e8393aca0d8dce9abc1 2 | -------------------------------------------------------------------------------- /tooling/docker_wsl_setup.md: -------------------------------------------------------------------------------- 1 | # Docker an Windows Subsystem for Linux (WSL) 2 | 3 | ## What is Docker? 4 | 5 | Docker allows us to run programs in isolation, as 'containers'. Think of them as lightweight versions of virtual machines, running only a few programs instead of EVERYTHING a normal computer does. 6 | 7 | A Docker Image is a template. It contains all the information needed to reproducibly create a container. Containers do not preserve internal state - each instance of a container starts from 0. 8 | 9 | Containers made from the same Image are identical copies of each other - you can have multiple instances running based on the same image. 10 | 11 | Containers do not persist data - every time you run a container, it starts from 0. To persi st data between container instances, you need volumes. 12 | 13 | Volumes are like virtual storage drives. They can replace part of the filesystem of a container. This means that when the container writes data, instead of ending up inside the container filesystem (and thus deleted if the container reboots), the data gets saved to the host. 14 | 15 | Volumes can be attached to multple containers simultaneously, allowing for example multiple database containers to share one storage area for configuration files. 16 | 17 | For Mac and Linux, setting up Docker is straightforward. For students usng Windows machines, you will need to setup WSL - Windows Subsystem for Linux. 18 | 19 | ## What is WSL? 20 | 21 | WSL lets us easily run a Linux VM on Windows. We will use this both to allow Docker to run and to ensure a consistent development environment for everyone on a team. 22 | 23 | You will install Docker on your Windows system, but clone your code, install node, etc inside of a Linux VM running in WSL. 24 | 25 | ## Pre-requisites 26 | 27 | - Know how to use a terminal (Powershell, Bash) 28 | 29 | ## Notes 30 | 31 | - Instead of installing `Ubuntu-22.04`, you can try setting up DistroD instead 32 | - All of the Docker and WSL setup commands will be run in Windows 33 | - The Docker install instructions contain links in the Pre-requisites section - make sure to go through those too. 34 | 35 | ## Links 36 | 37 | [Docker & WSL Instructions](https://docs.docker.com/desktop/windows/wsl/) 38 | 39 | [Distrod](https://github.com/nullpo-head/wsl-distrod) 40 | 41 | ## Workflow notes 42 | 43 | - You will always start the WSL instance 44 | - Once in the instnace, you will `cd` to where you cloned the repo, and from there use `code .` to launch vscode 45 | - vscode _should_ use bash for its terminals - if not then you will need another WSL terminal 46 | - all commands (docker, node, npm, git) must be run in the repo folder in your WSL instance 47 | -------------------------------------------------------------------------------- /tooling/documentation_tools.md: -------------------------------------------------------------------------------- 1 | # Documentation References 2 | 3 | * [Markdown - Basic Syntax](https://www.markdownguide.org/basic-syntax/#overview) 4 | * [Markdown - Extended Syntax](https://www.markdownguide.org/extended-syntax/#overview) 5 | * [Mermaid Diagram Syntax](https://mermaid-js.github.io/mermaid/#/flowchart) 6 | * [UML Class Diagram Tutorial](https://www.visual-paradigm.com/guide/uml-unified-modeling-language/uml-class-diagram-tutorial/) 7 | * [Emojis](https://emojipedia.org/) 8 | -------------------------------------------------------------------------------- /tooling/files.md: -------------------------------------------------------------------------------- 1 | # Files Explained 2 | 3 | ## [LICENSE](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/licensing-a-repository) 4 | 5 | * If a repository has no license, then all rights are reserved and it is not open-source or free 6 | * Public repositories on GitHub are often used to share open source software 7 | * MIT license gives users express permission to reuse code for any purpose, sometimes even if code is part of proprietary software 8 | * As long as users include the original copy of the MIT license in their distribution, they can make any changes or modifications to the code to suit their own needs 9 | * most simple open source license agreement 10 | 11 | ## [.sh Files](https://stackoverflow.com/questions/13805295/whats-a-sh-file) 12 | 13 | * bash shell script file 14 | * like batch files of Windows which can be executed in Linux or Unix 15 | * Bash is a Unix shell and command language which can run Shell Script files 16 | * scripting language commands file to be run by Unix shell 17 | * #!/bin/bash 18 | * uses Bourne Again Shell / bash 19 | * executing a .sh file is like writing the commands in the terminal 20 | 21 | ## types.ts 22 | 23 | * Use a named [export](https://bobbyhadz.com/blog/typescript-export-types) to export a type in TypeScript 24 | * The exported type can be imported by using a named import 25 | * [Generic Types ``](https://www.typescriptlang.org/docs/handbook/2/generics.html) 26 | * ??? TODO 27 | 28 | ## [tsconfig.json](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) 29 | 30 | * indicates that the directory is the root of a TypeScript project 31 | * specifies the root files and the compiler options required to compile the project 32 | * [generate tsconfig json file](https://stackoverflow.com/questions/36916989/how-can-i-generate-a-tsconfig-json-file) 33 | 34 | ## [package.json](https://docs.npmjs.com/cli/v8/configuring-npm/package-json) 35 | 36 | * for any node project 37 | * records important metadata about a project 38 | * defines functional attributes of a project that npm uses to install dependencies, run scripts, and identify the entry point to our package 39 | * [generate package.json](https://docs.npmjs.com/creating-a-package-json-file#creating-a-new-packagejson-file) 40 | * `npm init` 41 | * `npm install --save` 42 | * to install a dependency and automatically append it to your package.json's dependencies list 43 | * As of npm 5.0.0 (2017), installed modules are added as a dependency by default 44 | 45 | ## [package-lock.json](https://docs.npmjs.com/cli/v8/configuring-npm/package-lock-json) 46 | 47 | * automatically generated for any operations where npm modifies either the node_modules tree, or package.json 48 | * stores an exact, versioned dependency tree rather than using starred versioning like package.js [ref](https://stackoverflow.com/questions/44297803/what-is-the-role-of-the-package-lock-json) 49 | * package.json contains only your direct dependencies, not the dependencies of your dependencies 50 | 51 | * [dockerfile](https://docs.docker.com/engine/reference/builder/) 52 | * Docker can build images automatically by reading the instructions from a Dockerfile 53 | * text document that contains all the commands a user could call on the command line to assemble an image 54 | * `docker build .` 55 | * `FROM` [ref](https://docs.docker.com/engine/reference/builder/#from) 56 | * `ENV` [ref](https://docs.docker.com/engine/reference/builder/#environment-replacement) 57 | * `WORKDIR` [ref](https://docs.docker.com/engine/reference/builder/#workdir) 58 | 59 |

60 | 61 | ## Dot Files 62 | 63 | * hidden files/folders 64 | 65 | * [.npmignore](https://docs.npmjs.com/cli/v8/using-npm/developers#keeping-files-out-of-your-package) 66 | * keep stuff out of your package 67 | * npm will use .gitignore if no .npmignore 68 | * empty .npmignore file to override .gitignore 69 | * want to include something that is excluded by your .gitignore file 70 | 71 | * [.gitignore](https://git-scm.com/docs/gitignore) 72 | * [reference](https://salferrarello.com/gitignore-hidden-files-with-exceptions/) 73 | * specifies intentionally untracked files that Git should ignore 74 | 75 | * [.eslintrc.js](https://eslint.org/) 76 | * a configuration file for a tool named ESLINT 77 | * ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code, with the goal of making code more consistent and avoiding bug [red](https://stackoverflow.com/questions/58989583/what-is-eslintrc-js-in-react-native) 78 | * 79 | * 80 | * Lint, or a linter, is a static code analysis tool used to flag programming errors, bugs, stylistic errors and suspicious constructs 81 | 82 | * [dot env file](https://www.npmjs.com/package/dotenv) 83 | * customize your individual working environment variables 84 | * contains key/value pairs defining the project's required environment variables 85 | * In this file we set a variable with value and that you wouldn’t want to share with anyone, purpose of file is keep as secret and secure because in .env file we store our database password, username, API key etc… 86 | * [example](https://dev.to/aadilraza339/what-is-env-file-in-node-js-3h6c) 87 | * can use these variables in your Node JS project 88 | 89 | * dot dockerignore 90 | * Ignores files to speed up docker build process 91 | * otherwise the "Sending build context to Docker daemon" takes forever 92 | * [Using dockerignore](https://www.tutorialspoint.com/using-dockerignore-file) 93 | * [Docker Build Context (Why You Should Use Dockerignore)](https://www.howtogeek.com/devops/understanding-the-docker-build-context-why-you-should-use-dockerignore/) 94 | 95 | * [dot vscode folder](https://code.visualstudio.com/docs/getstarted/settings#_workspace-settings) 96 | * [Should I commit the .vscode folder to source control](https://stackoverflow.com/questions/32964920/should-i-commit-the-vscode-folder-to-source-control) 97 | * workspace settings 98 | * **launch.json** 99 | * [launch configs for debugging](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations) 100 | 101 | ## Other Files 102 | 103 | * data.json 104 | * [JSON (JavaScript Object Notation)](https://stackoverflow.com/questions/383692/what-is-json-and-what-is-it-used-for) 105 | * A collection of name/value pairs 106 | * An ordered list of values 107 | * random data for an object fitting the therapist schema i guess? 108 | -------------------------------------------------------------------------------- /tooling/git_tutorials.md: -------------------------------------------------------------------------------- 1 | # Git Tutorials 2 | 3 | - [Learn Git Branches](https://learngitbranching.js.org/?locale=en_US) 4 | - [Git Tutorial From Git](https://git-scm.com/docs/gittutorial) 5 | -------------------------------------------------------------------------------- /tooling/node_nvm_npm_pnpm.md: -------------------------------------------------------------------------------- 1 | # Node 2 | 3 | Node is a Javascript runtime that can run on servers. This lets us use one language across the whole stack, both frontend and backend. There are some differences between Node and the browser runtimes, but this gap has decreased as browser features get ported over. 4 | 5 | ## NVM 6 | 7 | NVM is the Node Version Manager. This tool helps manage different versions of Node, which really is only needed if you are working with really old projects that can't be upgraded or if you want to work with experimental versions, but it is way simpler than installing Node yourself. You can find installation instructions [here](https://github.com/nvm-sh/nvm). After installing, run `source ~/.bashrc`. 8 | 9 | Once you have installed NVM, you can install the latest Long Term Support (LTS) version of node using `nvm install --lts`. 10 | 11 | ## NPM 12 | 13 | NPM is the Node Package Manager. It manages dependencies and provides some additional tooling for JS projects, both frontend and backend. 14 | 15 | ## PNPM 16 | 17 | PNPM is a better version of NPM. Most of the improvement comes from package management - while NPM will redownload a dependency every tie it encounters it, PNPM will save all dependencies in one spot and reuse them if it can, making installation way faster. To install PNPM, run `npm i -g pnpm`. You can configure an alias in your shell, `alias npm='pnpm'`, to run PNPM instead of NPM while still using the `npm` command - this means if a script uses NPM, it will stay compatible. 18 | 19 | ## package.json 20 | 21 | A `package.json` file is a configuration file used in Node.js projects to define and manage project dependencies, scripts, and other metadata. It is typically located at the root of a project directory. It contains a JSON object with properties that describe different aspects of the project. This makes it easy to automate processing of the project metadata. 22 | 23 | The `package.json` file also serves as a record of the project's dependencies and their specific versions. This is crucial for version control systems like Git, as it allows other developers to easily set up the project with the correct dependencies. 24 | 25 | ## Dependencies 26 | 27 | The `dependencies` section lists the external libraries or packages that the project depends on. These dependencies are typically installed using a package manager like npm or yarn. The `package.json` file ensures that all necessary dependencies can be easily installed by running a single command. 28 | 29 | ## Scripts 30 | 31 | The `scripts` section defines custom commands that can be executed via the command line. These scripts can be used to automate common tasks, such as running tests, starting a development server, building the project, or deploying it to a server. By defining scripts in the `package.json` file, developers can easily run these commands without remembering complex command sequences. 32 | 33 | ## Project Initialization 34 | 35 | When starting a new project, creating a `package.json` file is often the first step. This file can be generated automatically using the `npm init` or `yarn init` command, which guides you through a series of prompts to set up the project metadata. 36 | 37 | In summary, the `package.json` file is a central component of Node.js projects. It provides important project metadata, manages dependencies, defines scripts for automation, and facilitates collaboration among developers. It is a crucial file for setting up, maintaining, and sharing Node.js projects. 38 | 39 | ## Example package.json 40 | 41 | ```json 42 | { 43 | "name": "my-node-project", 44 | "version": "1.0.0", 45 | "description": "A sample Node.js project", 46 | "author": "Your Name", 47 | "license": "MIT", 48 | "dependencies": { 49 | "express": "^4.17.1", 50 | "lodash": "^4.17.21" 51 | }, 52 | "devDependencies": { 53 | "nodemon": "^2.0.7", 54 | "eslint": "^7.32.0", 55 | "jest": "^27.2.5" 56 | }, 57 | "scripts": { 58 | "start": "node index.js", 59 | "dev": "nodemon index.js", 60 | "lint": "eslint .", 61 | "test": "jest" 62 | } 63 | } 64 | ``` 65 | 66 | In this example: 67 | 68 | - The project is named `my-node-project` and has a version of `1.0.0`. 69 | - The description and author fields provide additional information about the project. 70 | - The license field specifies that the project is licensed under the MIT license. 71 | - The dependencies section lists two dependencies: `express` and `lodash`. The version numbers indicate the minimum acceptable version range. 72 | - The devDependencies section lists three development dependencies: `nodemon`, `eslint`, and `jest`. These are dependencies required for development purposes, such as running tests or linting code. 73 | - The scripts section defines several commands: 74 | - The `start` script runs the project using the `node` command. 75 | - The `dev` script runs the project using `nodemon`, which automatically restarts the server when changes are made to the code during development. 76 | - The `lint` script runs the ESLint linter to check the code for potential errors or coding style violations. 77 | - The `test` script runs the Jest testing framework to execute tests written for the project. 78 | -------------------------------------------------------------------------------- /tooling/prisma_edge_functions_setup.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | In this page, we will discuss how to setup Prisma ORM to be compatible with Next.js functions running in the Edge Runtime (such as middleware.ts). Before we get started, we will provide definitions for some terminology needed for the setup, and then outline the individual components involved with this setup. 3 | 4 | ## What is Prisma ORM? 5 | Prisma ORM is an open-source relational mapping tool which is often used by Next.js projects managed by UTDesign EPICS. Its capabilities are extensive, including the ability to construct a database schema, create and drop database tables, query a database, and remove entries from a database. In this tutorial, no knowledge of how to use Prisma ORM will be required beyond knowing how to initialize the client. Also, keep in mind that whenever this page refers to 'Prisma', it is referring specifically to the ORM, not other Prisma services such as Accelerate or Pulse. If you want to read more about Prisma, you can check out their documentation at the link below: 6 | 7 | [Getting Started with Prisma](https://www.prisma.io/docs/getting-started) 8 | 9 | ## What is a Next.js Edge Function? 10 | A Next.js Edge Function is a function which runs utilizing the Next.js Edge Runtime. For this setup, all that is needed to be known about the Edge Runtime is that it is a lightweight version of the Next.js API which cannot make TCP requests and cannot perform I/O operations. If you want to read more about Edge Functions and their exact capabilities, you can check out their documentation at the link below: 11 | 12 | [Edge Runtime API](https://nextjs.org/docs/pages/api-reference/edge) 13 | 14 | # Prerequisites 15 | Before beginning the setup, a few perquisites need to be satisfied. First, make sure your Prisma imports are all up to date (THIS IS IMPORTANT). this can be done by running `npm update` or `yarn update` (depending on the package manager you are using). Next, we will need to install a few additional packages which are required to serve as an adapter for Prisma so that it can operate in the Edge Runtime: 16 | 17 | ``` 18 | npm install @prisma/adapter-neon 19 | npm install @neondatabase/serverless 20 | ``` 21 | ### Prisma Schema 22 | In your schema.prisma file, the generator and db should be updated to the following: 23 | 24 | ```prisma 25 | // schema.prisma 26 | generator client { 27 | provider = "prisma-client-js" 28 | previewFeatures = ["driverAdapters"] 29 | 30 | } 31 | 32 | datasource db { 33 | provider = "postgresql" 34 | url = env("POSTGRES_PRISMA_URL") 35 | } 36 | ``` 37 | 38 | NOTE: Make sure to change your database URL environment variable name to POSTGRES_PRISMA_URL in your .env file. The variable must be this name, as naming it something else leads to complications due to the influence of Vercel's naming conventions on Next.js itself. 39 | 40 | ### Package Scripts 41 | Next, you need to add a script to your `package.json` file: 42 | 43 | ```json 44 | // package.json 45 | { 46 | // ... 47 | "scripts": { 48 | // ... 49 | "postinstall": "prisma generate" 50 | } 51 | } 52 | ``` 53 | ### Enable WebAssembly 54 | You will also need to enable WebAssembly 5 in your `next.config.js`. Add the following section to the config: 55 | 56 | ```js 57 | // next.config.js 58 | /** @type {import('next').NextConfig} */ 59 | const nextConfig = { 60 | // ... 61 | webpack(config) { 62 | config.experiments ??= {} 63 | config.experiments.asyncWebAssembly = true 64 | 65 | return config 66 | } 67 | } 68 | module.exports = nextConfig 69 | ``` 70 | 71 | ### Add WebSocket Creation to docker-compose.yml 72 | Additionally, you will have to add a WebSocket container to your `docker-compose.yml`. Add the following section to your docker-compose: 73 | 74 | ```yml 75 | # docker-compose.yml 76 | # ... 77 | 78 | services: 79 | # ... 80 | # ... 81 | websockify: 82 | image: kamehb/websockify 83 | restart: unless-stopped 84 | command: "0.0.0.0:6432 db:5432" 85 | ports: 86 | - "6432:6432" 87 | depends_on: 88 | - db 89 | volumes: 90 | - websockify-data:/opt/websockify/data 91 | - websockify-config:/opt/websockify/config 92 | 93 | volumes: 94 | # ... 95 | websockify-data: 96 | driver: local 97 | websockify-config: 98 | driver: local 99 | ``` 100 | 101 | NOTE: If your database isn't hosted on port 5432, or your database service isn't named "db", you will have to change the `command: "0.0.0.0:6432 db:5432"` db:port to match your database name and/or port. 102 | 103 | After all these components have been added, you can now run `npx prisma init`. If you have already run this command in the past or have no prior database migrations, you must run `npx prisma migrate dev --name init`. (If you do not have a migrations directory, you do not have any prior migrations). 104 | 105 | You are now ready to setup and configure Prisma! 106 | 107 | # Setting up and Configuring Prisma 108 | Now that all the dependencies are out of the way, lets get into how to actually use Prisma in the Edge Runtime. In your function operating within the scope of the Edge Runtime, add the following code before your function declaration: 109 | 110 | ```ts 111 | const localDatabasePort = 6432; 112 | const localDatabaseHost = 'localhost'; 113 | neonConfig.fetchEndpoint = `http://${localDatabaseHost}:${localDatabasePort}`; 114 | neonConfig.useSecureWebSocket = false; // SET TO TRUE IN PROD 115 | neonConfig.pipelineConnect = false; 116 | neonConfig.wsProxy = 'localhost:6432'; 117 | const neon = new Pool({ connectionString: process.env.POSTGRES_PRISMA_URL }); 118 | const adapter = new PrismaNeon(neon); 119 | const client = new PrismaClient({ adapter }); 120 | 121 | // public async ... 122 | ``` 123 | ### Neon Config Options 124 | Neon is the database driver which Prisma uses to build their adapter for the Edge Runtime. In order for Neon to function, it requires a few configurations which we need to provide. Each configuration property is detailed below: 125 | 126 | `fetchEndpoint: URL`: set the server endpoint to be sent queries via http fetch (just in case). 127 | 128 | `useSecureWebSocket: boolean`: dictates whether the WebSocket should allow http:// connections. 129 | 130 | `pipelineConnect: "password" | false`: To speed up connection times, the driver will pipeline the first three messages to the database. You can try to set this setting to your database password, but if it doesn't work, just set it to false. 131 | 132 | `wsProxy: string`: set the server endpoint to be sent queries via the WebSocket. 133 | 134 | If you would like to view documentation on the full list of neonConfig properties, you can find this documentation in the link below: 135 | 136 | [Neon Serverless Config Properties](https://github.com/neondatabase/serverless/blob/main/CONFIG.md) 137 | 138 | ### Final Notes 139 | You should now be able to query Prisma as you normally would in any other function! If you would like to see an example of this implementation, it can be found here: 140 | 141 | [Demo of Prisma in a Next.js Edge Function](https://github.com/prisma/nextjs-edge-functions/tree/main) 142 | 143 | NOTE: Neon was not configured in this example, as the platform to which they are deploying their code takes care of this on their behalf. 144 | 145 | 146 | #### Written by Arif Nizami -------------------------------------------------------------------------------- /tooling/repo_cli_setup.md: -------------------------------------------------------------------------------- 1 | # Cloning repositories 2 | 3 | You will need to configure ssh in Ubuntu to use the same key pair that is configured in GitHub. 4 | 5 | You can either copy the file over to the Ubuntu instance, or point it at the original file on the Windows side. 6 | 7 | Whenever you see `<` and `>` in an example, those are often template indicators - you usually will not include them, they are meant to indicate a spot where you fill in a value specific to your use case. 8 | 9 | If you want to copy the key over, you can use the `cp` command. When in WSL, your Windows filesystem can be accessed at `/mnt/c/Users/`. 10 | 11 | Thus, you can copy the key as follows: `cp /mnt/c/Users//.ssh/ ~/.ssh/`. 12 | 13 | You may get an error about the destination directory not existing. Make sure you are in the Ubuntu home folder (you can run `pwd` to see your current working directory, and `cd ~` to go to your home directory if you are not already there). Use `ls -al` to check if there is a `.ssh` folder - if not, then you will need to create it. You can do this with `mkdir ~/.ssh`. 14 | 15 | This will copy the key to your Ubuntu home folder. After copying, we will need to update permissions. Run `chmod 600 ~/.ssh/`. 16 | 17 | If you don't want to bother copying, you can go straight to setting up the config file. 18 | 19 | In WSL, run `nano ~/.ssh/config`. This will open the nano text editor. 20 | 21 | You will add the following: 22 | 23 | ```text 24 | Host github.com 25 | IdentityFile ~/.ssh/ 26 | ``` 27 | 28 | Hit `Ctrl+X` to exit nano. It will prompt you to save the file. Type `y` and hit enter to finish saving. 29 | 30 | Note that if you didn't do the copy, instead of `~/.ssh/` you will provide `/mnt/c/Users//.ssh/`. 31 | 32 | You can either point the path at the copied key pair, or at the version sitting in Windows. 33 | 34 | You should now be able to run `git clone`, `git pull`, etc. 35 | 36 | To clone a repository, use `git clone ssh://git@github.com/UTDallasEpics/`. If this is your first time cloning from GitHub, it will prompt you to accept the fingerprint of the remote host. You will need to type `yes`. 37 | 38 | ## Setting up npm alias for pnpm 39 | 40 | In a WSL terminal, run `nano ~/.bashrc`. Navigate to the bottom of this file and add `alias npm="pnpm"`. This will automatically replace `npm` with `pnpm` whenever you execute `npm` in the terminal. 41 | -------------------------------------------------------------------------------- /webdev_playlist.md: -------------------------------------------------------------------------------- 1 | https://youtube.com/playlist?list=PL-HWLh_4CvrMsXKE-vY9ZWxfAYxirzDZW 2 | --------------------------------------------------------------------------------