├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── README.md ├── apps ├── backend │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── pb_data │ │ ├── data.db │ │ └── logs.db │ └── pocketbase └── visualizer │ ├── .svelte-kit │ ├── ambient.d.ts │ ├── generated │ │ ├── client │ │ │ ├── app.js │ │ │ ├── matchers.js │ │ │ └── nodes │ │ │ │ ├── 0.js │ │ │ │ ├── 1.js │ │ │ │ ├── 2.js │ │ │ │ ├── 3.js │ │ │ │ ├── 4.js │ │ │ │ ├── 5.js │ │ │ │ ├── 6.js │ │ │ │ └── 7.js │ │ ├── root.svelte │ │ └── server │ │ │ └── internal.js │ ├── tsconfig.json │ └── types │ │ ├── route_meta_data.json │ │ └── src │ │ └── routes │ │ ├── $types.d.ts │ │ ├── cluster_profile │ │ └── $types.d.ts │ │ ├── gui │ │ └── $types.d.ts │ │ ├── login │ │ └── $types.d.ts │ │ ├── oauth │ │ └── $types.d.ts │ │ ├── register │ │ └── $types.d.ts │ │ └── user_profile │ │ └── $types.d.ts │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── postcss.config.cjs │ ├── src │ ├── app.html │ ├── app.postcss │ ├── hooks.server.js │ ├── lib │ │ └── utils.js │ └── routes │ │ ├── +layout.server.js │ │ ├── +layout.svelte │ │ ├── +page.svelte │ │ ├── cluster_profile │ │ ├── +page.server.js │ │ └── +page.svelte │ │ ├── gui │ │ ├── +page.svelte │ │ └── +server.js │ │ ├── login │ │ ├── +page.server.js │ │ └── +page.svelte │ │ ├── oauth │ │ └── +server.js │ │ ├── register │ │ ├── +page.server.js │ │ └── +page.svelte │ │ └── user_profile │ │ ├── +page.server.js │ │ └── +page.svelte │ ├── static │ └── favicon.png │ ├── svelte.config.js │ ├── tailwind.config.cjs │ ├── tests │ └── test.js │ └── vite.config.js └── splash ├── assets ├── Kafkometry_Logo_Final.svg ├── alwin.jpeg ├── alwinAlt.jpeg ├── ben.jpg ├── dashboard.gif ├── dashboard.png ├── mitch.jpeg └── vincent.jpg ├── index.html ├── index.js ├── style.css └── teamgrid.css /.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:svelte/recommended', 7 | 'prettier' 8 | ], 9 | parser: '@typescript-eslint/parser', 10 | plugins: ['@typescript-eslint'], 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020, 14 | extraFileExtensions: ['.svelte'] 15 | }, 16 | env: { 17 | browser: true, 18 | es2017: true, 19 | node: true 20 | }, 21 | overrides: [ 22 | { 23 | files: ['*.svelte'], 24 | parser: 'svelte-eslint-parser', 25 | parserOptions: { 26 | parser: '@typescript-eslint/parser' 27 | } 28 | } 29 | ] 30 | }; 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | .svelte-kit/ 6 | .svelte-kit 7 | /package 8 | .env 9 | .env.* 10 | !.env.example 11 | vite.config.js.timestamp-* 12 | vite.config.ts.timestamp-* 13 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | resolution-mode=highest 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "pluginSearchDirs": ["."], 8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 9 | } 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kafkometry 2 | Kafkometry is a lightweight Apache Kafka metric visualizer created using Svelte/SvelteKit. 3 | 4 | 5 | # Table of Contents 6 | * [Features](https://github.com/oslabs-beta/Kafkometry#features) 7 | * [System Design](https://github.com/oslabs-beta/Kafkometry#system-design) 8 | * [Current Implementation](https://github.com/oslabs-beta/Kafkometry#current-implementation) 9 | * [Next Steps](https://github.com/oslabs-beta/Kafkometry#next-steps) 10 | * [Technologies Used](https://github.com/oslabs-beta/Kafkometry#technologies-used) 11 | * [Meet the Team](https://github.com/oslabs-beta/Kafkometry#meet-the-team) 12 | * [License](https://github.com/oslabs-beta/Kafkometry#license) 13 | 14 | # Features 15 | 16 | ![kafkometry_2](https://github.com/oslabs-beta/Kafkometry/assets/117921166/cd4f8b86-3404-49ce-98fe-15d0f4336581) 17 | 18 | * Live monitoring of key Apache Kafka metrics 19 | * Metric component customization using Grafana Desktop 20 | * Authentication using Google auth 21 | * A lightweight and user friendly UI/UX built in Svelte/SvelteKit 22 | 23 | ## View your metrics 24 | 1. Active Connections 25 | 26 | Screenshot 2023-07-26 at 11 22 54 AM 27 | 28 | 2. Partition Count 29 | 30 | Screenshot 2023-07-26 at 11 24 39 AM 31 | 32 | 3. Successful Authentications 33 | 34 | Screenshot 2023-07-26 at 11 24 56 AM 35 | 36 | 37 | 4. Bytes Sent 38 | 39 | Screenshot 2023-07-26 at 11 27 27 AM 40 | 41 | 5. Records Received 42 | 43 | Screenshot 2023-07-26 at 11 27 39 AM 44 | 45 | 6. Bytes Received 46 | 47 | Screenshot 2023-07-26 at 11 27 48 AM 48 | 49 | # System Design 50 | 51 | ![Kafkometry System Design](https://github.com/oslabs-beta/Kafkometry/assets/70918482/78c62153-12ca-4c8e-b4ef-15ae5eac91ce) 52 | 53 | Currently, the flow of data in our application is mapped by the diagram above. Our data flow begins with the Kafka cluster hosted on Confluent Cloud, with a Datagen connector that produces mock messages and events to the cloud-hosted cluster. Confluent Cloud conveniently has their own Confluent Cloud Metrics API, which exposes cluster metrics for availability at a specific HTTP endpoint. Prometheus is run with a prometheus.yml file, which is configured to set up Prometheus to scrape that exposed endpoint at a specific interval or rate. We then configure Grafana to set our local Prometheus instance as a data source, which allows the data that Prometheus scraped from the cloud-cluster to be available for visualization within Grafana. We then customize and configure Grafana dashboards, and embed them into our frontend application via iframes. 54 | 55 | # Current Implementation 56 | As of launch, our product and demo is currently set up with local instances of Prometheus and Grafana set up with YAML files to connect to our Confluent Cloud cluster via a Confluent Cloud API Key and Secret. To run this demo on their respective machines we currently require users to: 57 | 58 | 1. Host their clusters on Confluent Cloud 59 | 2. Configure a Metrics Viewer Role 60 | 3. Generate their own Cloud API Key and Secret 61 | 4. Install and run their own local, configured Prometheus instance 62 | 5. Create a Grafana Cloud account and select Prometheus as a data source 63 | 6. Fork and clone this repo 64 | 7. Customize and embed their own Grafana dashboards 65 | 8. Run `npm install` and `npm run dev` 66 | 67 | In its current state, there are a lot of steps that the user must complete to get Kafkometry up and running. Going forward, the Kafkometry team hopes to abstract many of these steps away to create a more seamless and intuitive user experience. We've thought about providing the necessary configuration YAML files to scrape from OUR Confluent Cloud cluster when running the users' own instances of Prometheus and Grafana so that our users will not have to create any accounts, but this still requires our users to install Prometheus on their own machine. Not to mention unsecure if we decided to post our prometheus.yml and Grafana configs that contain our Cloud API key and secret along with our Grafana credentials. Additionally, Confluent Cloud Metrics API has a rate limit on how often their endpoints can be scraped, therefore preventing the application from receiving realtime data. There's gotta be a better way! 68 | 69 | ## Next Steps 70 | For our next big patch, we have been working on containerizing our application with **Docker**! For demo purposes, we plan on spinning up a containerized cluster rather than hosting our cluster on Confluent Cloud to overcome the request rate limits imposed by Confluent Cloud Metrics API. We also plan on making the switch from using Grafana, to using Chart.js to design and render our own graphical interfaces for metrics for a superior user experience. With this containerized solution, our users can run the application off of images using a docker-compose.yaml (that we will provide) that can be run with a single `docker-compose -up` command, instead of downloading, configuring, and running their own instances of Prometheus, and eliminates the need to create their own Grafana account and dashboards. 71 | 72 | # Technologies Used 73 | ![my-skills](https://skillicons.dev/icons?i=svelte,tailwind,kafka,vite,prometheus,nodejs,netlify,grafana,&perline=4) 74 | 75 | # Meet the Team 76 | | Name | GitHub | LinkedIn | 77 | | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | 78 | | [**Benjamin Dunn**](https://github.com/benjam-26) |[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/benjam-26) |[![LinkedIn](https://img.shields.io/badge/linkedin-%230077B5.svg?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/ben-t-dunn/)| 79 | | [**Mitch Gruen**](https://github.com/mitchgruen) |[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/mitchgruen)|[![LinkedIn](https://img.shields.io/badge/linkedin-%230077B5.svg?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/mitch-gruen/)| 80 | | [**Alwin Zhao**](https://github.com/pijjon) |[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/pijjon)|[![LinkedIn](https://img.shields.io/badge/linkedin-%230077B5.svg?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/alwin-zhao) | 81 | | [**Vincent Do**](https://github.com/VDoCodes) |[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/VDoCodes)|[![LinkedIn](https://img.shields.io/badge/linkedin-%230077B5.svg?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/vincentydo) | 82 | 83 | # License 84 | [#MIT License](https://choosealicense.com/licenses/mit/) 85 | -------------------------------------------------------------------------------- /apps/backend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | /pb_data 12 | 13 | -------------------------------------------------------------------------------- /apps/backend/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.16.8 2 | 3 | - Fixed unique validator detailed error message not being returned when camelCase field name is used ([#2868](https://github.com/pocketbase/pocketbase/issues/2868)). 4 | 5 | - Updated the index parser to allow no space between the table name and the columns list ([#2864](https://github.com/pocketbase/pocketbase/discussions/2864#discussioncomment-6373736)). 6 | 7 | - Updated go deps. 8 | 9 | 10 | ## v0.16.7 11 | 12 | - Minor optimization for the list/search queries to use `rowid` with the `COUNT` statement when available. 13 | _This eliminates the temp B-TREE step when executing the query and for large datasets (eg. 150k) it could have 10x improvement (from ~580ms to ~60ms)._ 14 | 15 | 16 | ## v0.16.6 17 | 18 | - Fixed collection index column sort normalization in the Admin UI ([#2681](https://github.com/pocketbase/pocketbase/pull/2681); thanks @SimonLoir). 19 | 20 | - Removed unnecessary admins count in `apis.RequireAdminAuthOnlyIfAny()` middleware ([#2726](https://github.com/pocketbase/pocketbase/pull/2726); thanks @svekko). 21 | 22 | - Fixed `multipart/form-data` request bind not populating map array values ([#2763](https://github.com/pocketbase/pocketbase/discussions/2763#discussioncomment-6278902)). 23 | 24 | - Upgraded npm and Go dependencies. 25 | 26 | 27 | ## v0.16.5 28 | 29 | - Fixed the Admin UI serialization of implicit relation display fields ([#2675](https://github.com/pocketbase/pocketbase/issues/2675)). 30 | 31 | - Reset the Admin UI sort in case the active sort collection field is renamed or deleted. 32 | 33 | 34 | ## v0.16.4 35 | 36 | - Fixed the selfupdate command not working on Windows due to missing `.exe` in the extracted binary path ([#2589](https://github.com/pocketbase/pocketbase/discussions/2589)). 37 | _Note that the command on Windows will work on v0.16.4+ onwards, meaning that you still will have to update manually one more time to v0.16.4._ 38 | 39 | - Added `int64`, `int32`, `uint`, `uint64` and `uint32` support when scanning `types.DateTime` ([#2602](https://github.com/pocketbase/pocketbase/discussions/2602)) 40 | 41 | - Updated dependencies. 42 | 43 | 44 | ## v0.16.3 45 | 46 | - Fixed schema fields sort not working on Safari/Gnome Web ([#2567](https://github.com/pocketbase/pocketbase/issues/2567)). 47 | 48 | - Fixed default `PRAGMA`s not being applied for new connections ([#2570](https://github.com/pocketbase/pocketbase/discussions/2570)). 49 | 50 | 51 | ## v0.16.2 52 | 53 | - Fixed backups archive not excluding the local `backups` directory on Windows ([#2548](https://github.com/pocketbase/pocketbase/discussions/2548#discussioncomment-5979712)). 54 | 55 | - Changed file field to not use `dataTransfer.effectAllowed` when dropping files since it is not reliable and consistent across different OS and browsers ([#2541](https://github.com/pocketbase/pocketbase/issues/2541)). 56 | 57 | - Auto register the initial generated snapshot migration to prevent incorrectly reapplying the snapshot on Docker restart ([#2551](https://github.com/pocketbase/pocketbase/discussions/2551)). 58 | 59 | - Fixed missing view id field error message typo. 60 | 61 | 62 | ## v0.16.1 63 | 64 | - Fixed backup restore not working in a container environment when `pb_data` is mounted as volume ([#2519](https://github.com/pocketbase/pocketbase/issues/2519)). 65 | 66 | - Fixed Dart SDK realtime API preview example ([#2523](https://github.com/pocketbase/pocketbase/pull/2523); thanks @xFrann). 67 | 68 | - Fixed typo in the backups create panel ([#2526](https://github.com/pocketbase/pocketbase/pull/2526); thanks @dschissler). 69 | 70 | - Removed unnecessary slice length check in `list.ExistInSlice` ([#2527](https://github.com/pocketbase/pocketbase/pull/2527); thanks @KunalSin9h). 71 | 72 | - Avoid mutating the cached request data on OAuth2 user create ([#2535](https://github.com/pocketbase/pocketbase/discussions/2535)). 73 | 74 | - Fixed Export Collections "Download as JSON" ([#2540](https://github.com/pocketbase/pocketbase/issues/2540)). 75 | 76 | - Fixed file field drag and drop not working in Firefox and Safari ([#2541](https://github.com/pocketbase/pocketbase/issues/2541)). 77 | 78 | 79 | ## v0.16.0 80 | 81 | - Added automated backups (_+ cron rotation_) APIs and UI for the `pb_data` directory. 82 | The backups can be also initialized programmatically using `app.CreateBackup("backup.zip")`. 83 | There is also experimental restore method - `app.RestoreBackup("backup.zip")` (_currently works only on UNIX systems as it relies on execve_). 84 | The backups can be stored locally or in external S3 storage (_it has its own configuration, separate from the file uploads storage filesystem_). 85 | 86 | - Added option to limit the returned API fields using the `?fields` query parameter. 87 | The "fields picker" is applied for `SearchResult.Items` and every other JSON response. For example: 88 | ```js 89 | // original: {"id": "RECORD_ID", "name": "abc", "description": "...something very big...", "items": ["id1", "id2"], "expand": {"items": [{"id": "id1", "name": "test1"}, {"id": "id2", "name": "test2"}]}} 90 | // output: {"name": "abc", "expand": {"items": [{"name": "test1"}, {"name": "test2"}]}} 91 | const result = await pb.collection("example").getOne("RECORD_ID", { 92 | expand: "items", 93 | fields: "name,expand.items.name", 94 | }) 95 | ``` 96 | 97 | - Added new `./pocketbase update` command to selfupdate the prebuilt executable (with option to generate a backup of your `pb_data`). 98 | 99 | - Added new `./pocketbase admin` console command: 100 | ```sh 101 | // creates new admin account 102 | ./pocketbase admin create test@example.com 123456890 103 | 104 | // changes the password of an existing admin account 105 | ./pocketbase admin update test@example.com 0987654321 106 | 107 | // deletes single admin account (if exists) 108 | ./pocketbase admin delete test@example.com 109 | ``` 110 | 111 | - Added `apis.Serve(app, options)` helper to allow starting the API server programmatically. 112 | 113 | - Updated the schema fields Admin UI for "tidier" fields visualization. 114 | 115 | - Updated the logs "real" user IP to check for `Fly-Client-IP` header and changed the `X-Forward-For` header to use the first non-empty leftmost-ish IP as it the closest to the "real IP". 116 | 117 | - Added new `tools/archive` helper subpackage for managing archives (_currently works only with zip_). 118 | 119 | - Added new `tools/cron` helper subpackage for scheduling task using cron-like syntax (_this eventually may get exported in the future in a separate repo_). 120 | 121 | - Added new `Filesystem.List(prefix)` helper to retrieve a flat list with all files under the provided prefix. 122 | 123 | - Added new `App.NewBackupsFilesystem()` helper to create a dedicated filesystem abstraction for managing app data backups. 124 | 125 | - Added new `App.OnTerminate()` hook (_executed right before app termination, eg. on `SIGTERM` signal_). 126 | 127 | - Added `accept` file field attribute with the field MIME types ([#2466](https://github.com/pocketbase/pocketbase/pull/2466); thanks @Nikhil1920). 128 | 129 | - Added support for multiple files sort in the Admin UI ([#2445](https://github.com/pocketbase/pocketbase/issues/2445)). 130 | 131 | - Added support for multiple relations sort in the Admin UI. 132 | 133 | - Added `meta.isNew` to the OAuth2 auth JSON response to indicate a newly OAuth2 created PocketBase user. 134 | 135 | 136 | ## v0.15.3 137 | 138 | - Updated the Admin UI to use the latest JS SDK to resolve the `isNew` record field conflict ([#2385](https://github.com/pocketbase/pocketbase/discussions/2385)). 139 | 140 | - Fixed `editor` field fullscreen `z-index` ([#2410](https://github.com/pocketbase/pocketbase/issues/2410)). 141 | 142 | - Inserts the default app settings as part of the system init migration so that they are always available when accessed from within a user defined migration ([#2423](https://github.com/pocketbase/pocketbase/discussions/2423)). 143 | 144 | 145 | ## v0.15.2 146 | 147 | - Fixed View query `SELECT DISTINCT` identifiers parsing ([#2349-5706019](https://github.com/pocketbase/pocketbase/discussions/2349#discussioncomment-5706019)). 148 | 149 | - Fixed View collection schema incorrectly resolving multiple aliased fields originating from the same field source ([#2349-5707675](https://github.com/pocketbase/pocketbase/discussions/2349#discussioncomment-5707675)). 150 | 151 | - Added OAuth2 redirect fallback message to notify the user to go back to the app in case the browser window is not auto closed. 152 | 153 | 154 | ## v0.15.1 155 | 156 | - Trigger the related `Record` model realtime subscription events on [custom model struct](https://pocketbase.io/docs/custom-models/) save ([#2325](https://github.com/pocketbase/pocketbase/discussions/2325)). 157 | 158 | - Fixed `Ctrl + S` in the `editor` field not propagating the quick save shortcut to the parent form. 159 | 160 | - Added `⌘ + S` alias for the record quick save shortcut (_I have no Mac device to test it but it should work based on [`e.metaKey` docs](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/metaKey)_). 161 | 162 | - Enabled RTL for the TinyMCE editor ([#2327](https://github.com/pocketbase/pocketbase/issues/2327)). 163 | 164 | - Reduced the record form vertical layout shifts and slightly improved the rendering speed when loading multiple `relation` fields. 165 | 166 | - Enabled Admin UI assets cache. 167 | 168 | 169 | ## v0.15.0 170 | 171 | - Simplified the OAuth2 authentication flow in a single "all in one" call ([#55](https://github.com/pocketbase/pocketbase/issues/55)). 172 | Requires JS SDK v0.14.0+ or Dart SDK v0.9.0+. 173 | The manual code-token exchange flow is still supported but the SDK method is renamed to `authWithOAuth2Code()` (_to minimize the breaking changes the JS SDK has a function overload that will proxy the existing `authWithOauth2` calls to `authWithOAuth2Code`_). 174 | For more details and example, you could check https://pocketbase.io/docs/authentication/#oauth2-integration. 175 | 176 | - Added support for protected files ([#215](https://github.com/pocketbase/pocketbase/issues/215)). 177 | Requires JS SDK v0.14.0+ or Dart SDK v0.9.0+. 178 | It works with a short lived (~5min) file token passed as query param with the file url. 179 | For more details and example, you could check https://pocketbase.io/docs/files-handling/#protected-files. 180 | 181 | - **!** Fixed typo in `Record.WithUnkownData()` -> `Record.WithUnknownData()`. 182 | 183 | - Added simple loose wildcard search term support in the Admin UI. 184 | 185 | - Added auto "draft" to allow restoring previous record state in case of accidental reload or power outage. 186 | 187 | - Added `Ctrl + S` shortcut to save the record changes without closing the panel. 188 | 189 | - Added "drop files" support for the file upload field. 190 | 191 | - Refreshed the OAuth2 Admin UI. 192 | 193 | 194 | ## v0.14.5 195 | 196 | - Added checks for `nil` hooks in `forms.RecordUpsert` when used with custom `Dao` ([#2277](https://github.com/pocketbase/pocketbase/issues/2277)). 197 | 198 | - Fixed unique detailed field error not returned on record create failure ([#2287](https://github.com/pocketbase/pocketbase/discussions/2287)). 199 | 200 | 201 | ## v0.14.4 202 | 203 | - Fixed concurrent map write pannic on `list.ExistInSliceWithRegex()` cache ([#2272](https://github.com/pocketbase/pocketbase/issues/2272)). 204 | 205 | 206 | ## v0.14.3 207 | 208 | - Fixed Admin UI Logs `meta` visualization in Firefox ([#2221](https://github.com/pocketbase/pocketbase/issues/2221)). 209 | 210 | - Downgraded to v1 of the `aws/aws-sdk-go` package since v2 has compatibility issues with GCS ([#2231](https://github.com/pocketbase/pocketbase/issues/2231)). 211 | 212 | - Upgraded the GitHub action to use [min Go 1.20.3](https://github.com/golang/go/issues?q=milestone%3AGo1.20.3+label%3ACherryPickApproved) for the prebuilt executable since it contains some minor `net/http` security fixes. 213 | 214 | 215 | ## v0.14.2 216 | 217 | - Reverted part of the old `COALESCE` handling as a fallback to support empty string comparison with missing joined relation fields. 218 | 219 | 220 | ## v0.14.1 221 | 222 | - Fixed realtime events firing before the files upload completion. 223 | 224 | - Updated the underlying S3 lib to use `aws-sdk-go-v2` ([#1346](https://github.com/pocketbase/pocketbase/pull/1346); thanks @yuxiang-gao). 225 | 226 | - Updated TinyMCE to v6.4.1. 227 | 228 | - Updated the godoc of `Dao.Save*` methods. 229 | 230 | 231 | ## v0.14.0 232 | 233 | - Added _experimental_ Apple OAuth2 integration. 234 | 235 | - Added `@request.headers.*` filter rule support. 236 | 237 | - Added support for advanced unique constraints and indexes management ([#345](https://github.com/pocketbase/pocketbase/issues/345), [#544](https://github.com/pocketbase/pocketbase/issues/544)) 238 | 239 | - Simplified the collections fields UI to allow easier and quicker scaffolding of the data schema. 240 | 241 | - Deprecated `SchemaField.Unique`. Unique constraints are now managed via indexes. 242 | The `Unique` field is a no-op and will be removed in future version. 243 | 244 | - Removed the `COALESCE` wrapping from some of the generated filter conditions to make better use of the indexes ([#1939](https://github.com/pocketbase/pocketbase/issues/1939)). 245 | 246 | - Detect `id` aliased view columns as single `relation` fields ([#2029](https://github.com/pocketbase/pocketbase/discussions/2029)). 247 | 248 | - Optimized single relation lookups. 249 | 250 | - Normalized record values on `maxSelect` field option change (`select`, `file`, `relation`). 251 | When changing **from single to multiple** all already inserted single values are converted to an array. 252 | When changing **from multiple to single** only the last item of the already inserted array items is kept. 253 | 254 | - Changed the cost/round factor of bcrypt hash generation from 13 to 12 since several users complained about the slow authWithPassword responses on lower spec hardware. 255 | _The change will affect only new users. Depending on the demand, we might make it configurable from the auth options._ 256 | 257 | - Simplified the default mail template styles to allow more control over the template layout ([#1904](https://github.com/pocketbase/pocketbase/issues/1904)). 258 | 259 | - Added option to explicitly set the record id from the Admin UI ([#2118](https://github.com/pocketbase/pocketbase/issues/2118)). 260 | 261 | - Added `migrate history-sync` command to clean `_migrations` history table from deleted migration files references. 262 | 263 | - Added new fields to the `core.RecordAuthWithOAuth2Event` struct: 264 | ``` 265 | IsNewRecord bool, // boolean field indicating whether the OAuth2 action created a new auth record 266 | ProviderName string, // the name of the OAuth2 provider (eg. "google") 267 | ProviderClient auth.Provider, // the loaded Provider client instance 268 | ``` 269 | 270 | - Added CGO linux target for the prebuilt executable. 271 | 272 | - **!** Renamed `daos.GetTableColumns()` to `daos.TableColumns()` for consistency with the other Dao table related helpers. 273 | 274 | - **!** Renamed `daos.GetTableInfo()` to `daos.TableInfo()` for consistency with the other Dao table related helpers. 275 | 276 | - **!** Changed `types.JsonArray` to support specifying a generic type, aka. `types.JsonArray[T]`. 277 | If you have previously used `types.JsonArray`, you'll have to update it to `types.JsonArray[any]`. 278 | 279 | - **!** Registered the `RemoveTrailingSlash` middleware only for the `/api/*` routes since it is causing issues with subpath file serving endpoints ([#2072](https://github.com/pocketbase/pocketbase/issues/2072)). 280 | 281 | - **!** Changed the request logs `method` value to UPPERCASE, eg. "get" => "GET" ([#1956](https://github.com/pocketbase/pocketbase/discussions/1956)). 282 | 283 | - Other minor UI improvements. 284 | 285 | 286 | ## v0.13.4 287 | 288 | - Removed eager unique collection name check to support lazy validation during bulk import. 289 | 290 | 291 | ## v0.13.3 292 | 293 | - Fixed view collections import ([#2044](https://github.com/pocketbase/pocketbase/issues/2044)). 294 | 295 | - Updated the records picker Admin UI to show properly view collection relations. 296 | 297 | 298 | ## v0.13.2 299 | 300 | - Fixed Admin UI js error when selecting multiple `file` field as `relation` "Display fields" ([#1989](https://github.com/pocketbase/pocketbase/issues/1989)). 301 | 302 | 303 | ## v0.13.1 304 | 305 | - Added `HEAD` request method support for the `/api/files/:collection/:recordId/:filename` route ([#1976](https://github.com/pocketbase/pocketbase/discussions/1976)). 306 | 307 | 308 | ## v0.13.0 309 | 310 | - Added new "View" collection type allowing you to create a read-only collection from a custom SQL `SELECT` statement. It supports: 311 | - aggregations (`COUNT()`, `MIN()`, `MAX()`, `GROUP BY`, etc.) 312 | - column and table aliases 313 | - CTEs and subquery expressions 314 | - auto `relation` fields association 315 | - `file` fields proxying (up to 5 linked relations, eg. view1->view2->...->base) 316 | - `filter`, `sort` and `expand` 317 | - List and View API rules 318 | 319 | - Added auto fail/retry (default to 8 attempts) for the `SELECT` queries to gracefully handle the `database is locked` errors ([#1795](https://github.com/pocketbase/pocketbase/discussions/1795#discussioncomment-4882169)). 320 | _The default max attempts can be accessed or changed via `Dao.MaxLockRetries`._ 321 | 322 | - Added default max query execution timeout (30s). 323 | _The default timeout can be accessed or changed via `Dao.ModelQueryTimeout`._ 324 | _For the prebuilt executables it can be also changed via the `--queryTimeout=10` flag._ 325 | 326 | - Added support for `dao.RecordQuery(collection)` to scan directly the `One()` and `All()` results in `*models.Record` or `[]*models.Record` without the need of explicit `NullStringMap`. 327 | 328 | - Added support to overwrite the default file serve headers if an explicit response header is set. 329 | 330 | - Added file thumbs when visualizing `relation` display file fields. 331 | 332 | - Added "Min select" `relation` field option. 333 | 334 | - Enabled `process.env` in JS migrations to allow accessing `os.Environ()`. 335 | 336 | - Added `UploadedFiles` field to the `RecordCreateEvent` and `RecordUpdateEvent` event structs. 337 | 338 | - **!** Moved file upload after the record persistent to allow setting custom record id safely from the `OnModelBeforeCreate` hook. 339 | 340 | - **!** Changed `System.GetFile()` to return directly `*blob.Reader` instead of the `io.ReadCloser` interface. 341 | 342 | - **!** Changed `To`, `Cc` and `Bcc` of `mailer.Message` to `[]mail.Address` for consistency and to allow multiple recipients and optional name. 343 | 344 | If you are sending custom emails, you'll have to replace: 345 | ```go 346 | message := &mailer.Message{ 347 | ... 348 | 349 | // (old) To: mail.Address{Address: "to@example.com"} 350 | To: []mail.Address{{Address: "to@example.com", Name: "Some optional name"}}, 351 | 352 | // (old) Cc: []string{"cc@example.com"} 353 | Cc: []mail.Address{{Address: "cc@example.com", Name: "Some optional name"}}, 354 | 355 | // (old) Bcc: []string{"bcc@example.com"} 356 | Bcc: []mail.Address{{Address: "bcc@example.com", Name: "Some optional name"}}, 357 | 358 | ... 359 | } 360 | ``` 361 | 362 | - **!** Refactored the Authentik integration as a more generic "OpenID Connect" provider (`oidc`) to support any OIDC provider (Okta, Keycloak, etc.). 363 | _If you've previously used Authentik, make sure to rename the provider key in your code to `oidc`._ 364 | _To enable more than one OIDC provider you can use the additional `oidc2` and `oidc3` provider keys._ 365 | 366 | - **!** Removed the previously deprecated `Dao.Block()` and `Dao.Continue()` helpers in favor of `Dao.NonconcurrentDB()`. 367 | 368 | - Updated the internal redirects to allow easier subpath deployment when behind a reverse proxy. 369 | 370 | - Other minor Admin UI improvements. 371 | 372 | 373 | ## v0.12.3 374 | 375 | - Fixed "Toggle column" reactivity when navigating between collections ([#1836](https://github.com/pocketbase/pocketbase/pull/1836)). 376 | 377 | - Logged the current datetime on server start ([#1822](https://github.com/pocketbase/pocketbase/issues/1822)). 378 | 379 | 380 | ## v0.12.2 381 | 382 | - Fixed the "Clear" button of the datepicker component not clearing the value ([#1730](https://github.com/pocketbase/pocketbase/discussions/1730)). 383 | 384 | - Increased slightly the fields contrast ([#1742](https://github.com/pocketbase/pocketbase/issues/1742)). 385 | 386 | - Auto close the multi-select dropdown if "Max select" is reached. 387 | 388 | 389 | ## v0.12.1 390 | 391 | - Fixed js error on empty relation save. 392 | 393 | - Fixed `overlay-active` css class not being removed on nested overlay panel close ([#1718](https://github.com/pocketbase/pocketbase/issues/1718)). 394 | 395 | - Added the collection name in the page title ([#1711](https://github.com/pocketbase/pocketbase/issues/1711)). 396 | 397 | 398 | ## v0.12.0 399 | 400 | - Refactored the relation picker UI to allow server-side search, sort, create, update and delete of relation records ([#976](https://github.com/pocketbase/pocketbase/issues/976)). 401 | 402 | - Added new `RelationOptions.DisplayFields` option to specify custom relation field(s) visualization in the Admin UI. 403 | 404 | - Added Authentik OAuth2 provider ([#1377](https://github.com/pocketbase/pocketbase/pull/1377); thanks @pr0ton11). 405 | 406 | - Added LiveChat OAuth2 provider ([#1573](https://github.com/pocketbase/pocketbase/pull/1573); thanks @mariosant). 407 | 408 | - Added Gitea OAuth2 provider ([#1643](https://github.com/pocketbase/pocketbase/pull/1643); thanks @hlanderdev). 409 | 410 | - Added PDF file previews ([#1548](https://github.com/pocketbase/pocketbase/pull/1548); thanks @mjadobson). 411 | 412 | - Added video and audio file previews. 413 | 414 | - Added rich text editor (`editor`) field for HTML content based on TinyMCE ([#370](https://github.com/pocketbase/pocketbase/issues/370)). 415 | _Currently the new field doesn't have any configuration options or validations but this may change in the future depending on how devs ended up using it._ 416 | 417 | - Added "Duplicate" Collection and Record options in the Admin UI ([#1656](https://github.com/pocketbase/pocketbase/issues/1656)). 418 | 419 | - Added `filesystem.GetFile()` helper to read files through the FileSystem abstraction ([#1578](https://github.com/pocketbase/pocketbase/pull/1578); thanks @avarabyeu). 420 | 421 | - Added new auth event hooks for finer control and more advanced auth scenarios handling: 422 | 423 | ```go 424 | // auth record 425 | OnRecordBeforeAuthWithPasswordRequest() 426 | OnRecordAfterAuthWithPasswordRequest() 427 | OnRecordBeforeAuthWithOAuth2Request() 428 | OnRecordAfterAuthWithOAuth2Request() 429 | OnRecordBeforeAuthRefreshRequest() 430 | OnRecordAfterAuthRefreshRequest() 431 | 432 | // admin 433 | OnAdminBeforeAuthWithPasswordRequest() 434 | OnAdminAfterAuthWithPasswordRequest() 435 | OnAdminBeforeAuthRefreshRequest() 436 | OnAdminAfterAuthRefreshRequest() 437 | OnAdminBeforeRequestPasswordResetRequest() 438 | OnAdminAfterRequestPasswordResetRequest() 439 | OnAdminBeforeConfirmPasswordResetRequest() 440 | OnAdminAfterConfirmPasswordResetRequest() 441 | ``` 442 | 443 | - Added `models.Record.CleanCopy()` helper that creates a new record copy with only the latest data state of the existing one and all other options reset to their defaults. 444 | 445 | - Added new helper `apis.RecordAuthResponse(app, httpContext, record, meta)` to return a standard Record auth API response ([#1623](https://github.com/pocketbase/pocketbase/issues/1623)). 446 | 447 | - Refactored `models.Record` expand and data change operations to be concurrent safe. 448 | 449 | - Refactored all `forms` Submit interceptors to use a generic data type as their payload. 450 | 451 | - Added several `store.Store` helpers: 452 | ```go 453 | store.Reset(newData map[string]T) 454 | store.Length() int 455 | store.GetAll() map[string]T 456 | ``` 457 | 458 | - Added "tags" support for all Record and Model related event hooks. 459 | 460 | The "tags" allow registering event handlers that will be called only on matching table name(s) or colleciton id(s)/name(s). 461 | For example: 462 | ```go 463 | app.OnRecordBeforeCreateRequest("articles").Add(func(e *core.RecordCreateEvent) error { 464 | // called only on "articles" record creation 465 | log.Println(e.Record) 466 | return nil 467 | }) 468 | ``` 469 | For all those event hooks `*hook.Hook` was replaced with `*hooks.TaggedHook`, but the hook methods signatures are the same so it should behave as it was previously if no tags were specified. 470 | 471 | - **!** Fixed the `json` field **string** value normalization ([#1703](https://github.com/pocketbase/pocketbase/issues/1703)). 472 | 473 | In order to support seamlessly both `application/json` and `multipart/form-data` 474 | requests, the following normalization rules are applied if the `json` field is a 475 | **plain string value**: 476 | 477 | - "true" is converted to the json `true` 478 | - "false" is converted to the json `false` 479 | - "null" is converted to the json `null` 480 | - "[1,2,3]" is converted to the json `[1,2,3]` 481 | - "{\"a\":1,\"b\":2}" is converted to the json `{"a":1,"b":2}` 482 | - numeric strings are converted to json number 483 | - double quoted strings are left as they are (aka. without normalizations) 484 | - any other string (empty string too) is double quoted 485 | 486 | Additionally, the "Nonempty" `json` field constraint now checks for `null`, `[]`, `{}` and `""` (empty string). 487 | 488 | - Added `aria-label` to some of the buttons in the Admin UI for better accessibility ([#1702](https://github.com/pocketbase/pocketbase/pull/1702); thanks @ndarilek). 489 | 490 | - Updated the filename extension checks in the Admin UI to be case-insensitive ([#1707](https://github.com/pocketbase/pocketbase/pull/1707); thanks @hungcrush). 491 | 492 | - Other minor improvements (more detailed API file upload errors, UI optimizations, docs improvements, etc.) 493 | 494 | 495 | ## v0.11.4 496 | 497 | - Fixed cascade delete for rel records with the same id as the main record ([#1689](https://github.com/pocketbase/pocketbase/issues/1689)). 498 | 499 | 500 | ## v0.11.3 501 | 502 | - Fix realtime API panic on concurrent clients iteration ([#1628](https://github.com/pocketbase/pocketbase/issues/1628)) 503 | 504 | - `app.SubscriptionsBroker().Clients()` now returns a shallow copy of the underlying map. 505 | 506 | - Added `Discard()` and `IsDiscarded()` helper methods to the `subscriptions.Client` interface. 507 | 508 | - Slow clients should no longer "block" the main action completion. 509 | 510 | 511 | ## v0.11.2 512 | 513 | - Fixed `fs.DeleteByPrefix()` hang on invalid S3 settings ([#1575](https://github.com/pocketbase/pocketbase/discussions/1575#discussioncomment-4661089)). 514 | 515 | - Updated file(s) delete to run in the background on record/collection delete to avoid blocking the delete model transaction. 516 | _Currently the cascade files delete operation is treated as "non-critical" and in case of an error it is just logged during debug._ 517 | _This will be improved in the near future with the planned async job queue implementation._ 518 | 519 | 520 | ## v0.11.1 521 | 522 | - Unescaped path parameter values ([#1552](https://github.com/pocketbase/pocketbase/issues/1552)). 523 | 524 | 525 | ## v0.11.0 526 | 527 | - Added `+` and `-` body field modifiers for `number`, `files`, `select` and `relation` fields. 528 | ```js 529 | { 530 | // oldValue + 2 531 | "someNumber+": 2, 532 | 533 | // oldValue + ["id1", "id2"] - ["id3"] 534 | "someRelation+": ["id1", "id2"], 535 | "someRelation-": ["id3"], 536 | 537 | // delete single file by its name (file fields supports only the "-" modifier!) 538 | "someFile-": "filename.png", 539 | } 540 | ``` 541 | _Note1: `@request.data.someField` will contain the final resolved value._ 542 | 543 | _Note2: The old index (`"field.0":null`) and filename (`"field.filename.png":null`) based suffixed syntax for deleting files is still supported._ 544 | 545 | - ! Added support for multi-match/match-all request data and collection multi-valued fields (`select`, `relation`) conditions. 546 | If you want a "at least one of" type of condition, you can prefix the operator with `?`. 547 | ```js 548 | // for each someRelA.someRelB record require the "status" field to be "active" 549 | someRelA.someRelB.status = "active" 550 | 551 | // OR for "at least one of" condition 552 | someRelA.someRelB.status ?= "active" 553 | ``` 554 | _**Note: Previously the behavior for multi-valued fields was as the "at least one of" type. 555 | The release comes with system db migration that will update your existing API rules (if needed) to preserve the compatibility. 556 | If you have multi-select or multi-relation filter checks in your client-side code and want to preserve the old behavior, you'll have to prefix with `?` your operators.**_ 557 | 558 | - Added support for querying `@request.data.someRelField.*` relation fields. 559 | ```js 560 | // example submitted data: {"someRel": "REL_RECORD_ID"} 561 | @request.data.someRel.status = "active" 562 | ``` 563 | 564 | - Added `:isset` modifier for the static request data fields. 565 | ```js 566 | // prevent changing the "role" field 567 | @request.data.role:isset = false 568 | ``` 569 | 570 | - Added `:length` modifier for the arrayable request data and collection fields (`select`, `file`, `relation`). 571 | ```js 572 | // example submitted data: {"someSelectField": ["val1", "val2"]} 573 | @request.data.someSelectField:length = 2 574 | 575 | // check existing record field length 576 | someSelectField:length = 2 577 | ``` 578 | 579 | - Added `:each` modifier support for the multi-`select` request data and collection field. 580 | ```js 581 | // check if all selected rows has "pb_" prefix 582 | roles:each ~ 'pb_%' 583 | ``` 584 | 585 | - Improved the Admin UI filters autocomplete. 586 | 587 | - Added `@random` sort key for `RANDOM()` sorted list results. 588 | 589 | - Added Strava OAuth2 provider ([#1443](https://github.com/pocketbase/pocketbase/pull/1443); thanks @szsascha). 590 | 591 | - Added Gitee OAuth2 provider ([#1448](https://github.com/pocketbase/pocketbase/pull/1448); thanks @yuxiang-gao). 592 | 593 | - Added IME status check to the textarea keydown handler ([#1370](https://github.com/pocketbase/pocketbase/pull/1370); thanks @tenthree). 594 | 595 | - Added `filesystem.NewFileFromBytes()` helper ([#1420](https://github.com/pocketbase/pocketbase/pull/1420); thanks @dschissler). 596 | 597 | - Added support for reordering uploaded multiple files. 598 | 599 | - Added `webp` to the default images mime type presets list ([#1469](https://github.com/pocketbase/pocketbase/pull/1469); thanks @khairulhaaziq). 600 | 601 | - Added the OAuth2 refresh token to the auth meta response ([#1487](https://github.com/pocketbase/pocketbase/issues/1487)). 602 | 603 | - Fixed the text wrapping in the Admin UI listing searchbar ([#1416](https://github.com/pocketbase/pocketbase/issues/1416)). 604 | 605 | - Fixed number field value output in the records listing ([#1447](https://github.com/pocketbase/pocketbase/issues/1447)). 606 | 607 | - Fixed duplicated settings view pages caused by uncompleted transitions ([#1498](https://github.com/pocketbase/pocketbase/issues/1498)). 608 | 609 | - Allowed sending `Authorization` header with the `/auth-with-password` record and admin login requests ([#1494](https://github.com/pocketbase/pocketbase/discussions/1494)). 610 | 611 | - `migrate down` now reverts migrations in the applied order. 612 | 613 | - Added additional list-bucket check in the S3 config test API. 614 | 615 | - Other minor improvements. 616 | 617 | 618 | ## v0.10.4 619 | 620 | - Fixed `Record.MergeExpand` panic when the main model expand map is not initialized ([#1365](https://github.com/pocketbase/pocketbase/issues/1365)). 621 | 622 | 623 | ## v0.10.3 624 | 625 | - ! Renamed the metadata key `original_filename` to `original-filename` due to an S3 file upload error caused by the underscore character ([#1343](https://github.com/pocketbase/pocketbase/pull/1343); thanks @yuxiang-gao). 626 | 627 | - Fixed request verification docs api url ([#1332](https://github.com/pocketbase/pocketbase/pull/1332); thanks @JoyMajumdar2001) 628 | 629 | - Excluded `collectionId` and `collectionName` from the displayable relation props list ([1322](https://github.com/pocketbase/pocketbase/issues/1322); thanks @dhall2). 630 | 631 | 632 | ## v0.10.2 633 | 634 | - Fixed nested multiple expands with shared path ([#586](https://github.com/pocketbase/pocketbase/issues/586#issuecomment-1357784227)). 635 | A new helper method `models.Record.MergeExpand(map[string]any)` was also added to simplify the expand handling and unit testing. 636 | 637 | 638 | ## v0.10.1 639 | 640 | - Fixed nested transactions deadlock when authenticating with OAuth2 ([#1291](https://github.com/pocketbase/pocketbase/issues/1291)). 641 | 642 | 643 | ## v0.10.0 644 | 645 | - Added `/api/health` endpoint (thanks @MarvinJWendt). 646 | 647 | - Added support for SMTP `LOGIN` auth for Microsoft/Outlook and other providers that don't support the `PLAIN` auth method ([#1217](https://github.com/pocketbase/pocketbase/discussions/1217#discussioncomment-4387970)). 648 | 649 | - Reduced memory consumption (you can expect ~20% less allocated memory). 650 | 651 | - Added support for split (concurrent and nonconcurrent) DB connections pool increasing even further the concurrent throughput without blocking reads on heavy write load. 652 | 653 | - Improved record references delete performance. 654 | 655 | - Removed the unnecessary parenthesis in the generated filter SQL query, reducing the "_parse stack overflow_" errors. 656 | 657 | - Fixed `~` expressions backslash literal escaping ([#1231](https://github.com/pocketbase/pocketbase/discussions/1231)). 658 | 659 | - Refactored the `core.app.Bootstrap()` to be called before starting the cobra commands ([#1267](https://github.com/pocketbase/pocketbase/discussions/1267)). 660 | 661 | - ! Changed `pocketbase.NewWithConfig(config Config)` to `pocketbase.NewWithConfig(config *Config)` and added 4 new config settings: 662 | ```go 663 | DataMaxOpenConns int // default to core.DefaultDataMaxOpenConns 664 | DataMaxIdleConns int // default to core.DefaultDataMaxIdleConns 665 | LogsMaxOpenConns int // default to core.DefaultLogsMaxOpenConns 666 | LogsMaxIdleConns int // default to core.DefaultLogsMaxIdleConns 667 | ``` 668 | 669 | - Added new helper method `core.App.IsBootstrapped()` to check the current app bootstrap state. 670 | 671 | - ! Changed `core.NewBaseApp(dir, encryptionEnv, isDebug)` to `NewBaseApp(config *BaseAppConfig)`. 672 | 673 | - ! Removed `rest.UploadedFile` struct (see below `filesystem.File`). 674 | 675 | - Added generic file resource struct that allows loading and uploading file content from 676 | different sources (at the moment multipart/form-data requests and from the local filesystem). 677 | ``` 678 | filesystem.File{} 679 | filesystem.NewFileFromPath(path) 680 | filesystem.NewFileFromMultipart(multipartHeader) 681 | filesystem/System.UploadFile(file) 682 | ``` 683 | 684 | - Refactored `forms.RecordUpsert` to allow more easily loading and removing files programmatically. 685 | ``` 686 | forms.RecordUpsert.AddFiles(key, filesystem.File...) // add new filesystem.File to the form for upload 687 | forms.RecordUpsert.RemoveFiles(key, filenames...) // marks the filenames for deletion 688 | ``` 689 | 690 | - Trigger the `password` validators if any of the others password change fields is set. 691 | 692 | 693 | ## v0.9.2 694 | 695 | - Fixed field column name conflict on record deletion ([#1220](https://github.com/pocketbase/pocketbase/discussions/1220)). 696 | 697 | 698 | ## v0.9.1 699 | 700 | - Moved the record file upload and delete out of the db transaction to minimize the locking times. 701 | 702 | - Added `Dao` query semaphore and base fail/retry handling to improve the concurrent writes throughput ([#1187](https://github.com/pocketbase/pocketbase/issues/1187)). 703 | 704 | - Fixed records cascade deletion when there are "A<->B" relation references. 705 | 706 | - Replaced `c.QueryString()` with `c.QueryParams().Encode()` to allow loading middleware modified query parameters in the default crud actions ([#1210](https://github.com/pocketbase/pocketbase/discussions/1210)). 707 | 708 | - Fixed the datetime field not triggering the `onChange` event on manual field edit and added a "Clear" button ([#1219](https://github.com/pocketbase/pocketbase/issues/1219)). 709 | 710 | - Updated the GitHub goreleaser action to use go 1.19.4 since it comes with [some security fixes](https://github.com/golang/go/issues?q=milestone%3AGo1.19.4+label%3ACherryPickApproved). 711 | 712 | 713 | ## v0.9.0 714 | 715 | - Fixed concurrent multi-relation cascade update/delete ([#1138](https://github.com/pocketbase/pocketbase/issues/1138)). 716 | 717 | - Added the raw OAuth2 user data (`meta.rawUser`) and OAuth2 access token (`meta.accessToken`) to the auth response ([#654](https://github.com/pocketbase/pocketbase/discussions/654)). 718 | 719 | - `BaseModel.UnmarkAsNew()` method was renamed to `BaseModel.MarkAsNotNew()`. 720 | Additionally, to simplify the insert model queries with custom IDs, it is no longer required to call `MarkAsNew()` for manually initialized models with set ID since now this is the default state. 721 | When the model is populated with values from the database (eg. after row `Scan`) it will be marked automatically as "not new". 722 | 723 | - Added `Record.OriginalCopy()` method that returns a new `Record` copy populated with the initially loaded record data (useful if you want to compare old and new field values). 724 | 725 | - Added new event hooks: 726 | ```go 727 | app.OnBeforeBootstrap() 728 | app.OnAfterBootstrap() 729 | app.OnBeforeApiError() 730 | app.OnAfterApiError() 731 | app.OnRealtimeDisconnectRequest() 732 | app.OnRealtimeBeforeMessageSend() 733 | app.OnRealtimeAfterMessageSend() 734 | app.OnRecordBeforeRequestPasswordResetRequest() 735 | app.OnRecordAfterRequestPasswordResetRequest() 736 | app.OnRecordBeforeConfirmPasswordResetRequest() 737 | app.OnRecordAfterConfirmPasswordResetRequest() 738 | app.OnRecordBeforeRequestVerificationRequest() 739 | app.OnRecordAfterRequestVerificationRequest() 740 | app.OnRecordBeforeConfirmVerificationRequest() 741 | app.OnRecordAfterConfirmVerificationRequest() 742 | app.OnRecordBeforeRequestEmailChangeRequest() 743 | app.OnRecordAfterRequestEmailChangeRequest() 744 | app.OnRecordBeforeConfirmEmailChangeRequest() 745 | app.OnRecordAfterConfirmEmailChangeRequest() 746 | ``` 747 | 748 | - The original uploaded file name is now stored as metadata under the `original_filename` key. It could be accessed via: 749 | ```go 750 | fs, _ := app.NewFilesystem() 751 | defer fs.Close() 752 | 753 | attrs, _ := fs.Attributes(fikeKey) 754 | attrs.Metadata["original_name"] 755 | ``` 756 | 757 | - Added support for `Partial/Range` file requests ([#1125](https://github.com/pocketbase/pocketbase/issues/1125)). 758 | This is a minor breaking change if you are using `filesystem.Serve` (eg. as part of a custom `OnFileDownloadRequest` hook): 759 | ```go 760 | // old 761 | filesystem.Serve(res, e.ServedPath, e.ServedName) 762 | 763 | // new 764 | filesystem.Serve(res, req, e.ServedPath, e.ServedName) 765 | ``` 766 | 767 | - Refactored the `migrate` command to support **external JavaScript migration files** using an embedded JS interpreter ([goja](https://github.com/dop251/goja)). 768 | This allow writing custom migration scripts such as programmatically creating collections, 769 | initializing default settings, running data imports, etc., with a JavaScript API very similar to the Go one (_more documentation will be available soon_). 770 | 771 | The `migrate` command is available by default for the prebuilt executable, 772 | but if you use PocketBase as framework you need register it manually: 773 | ```go 774 | migrationsDir := "" // default to "pb_migrations" (for js) and "migrations" (for go) 775 | 776 | // load js files if you want to allow loading external JavaScript migrations 777 | jsvm.MustRegisterMigrations(app, &jsvm.MigrationsOptions{ 778 | Dir: migrationsDir, 779 | }) 780 | 781 | // register the `migrate` command 782 | migratecmd.MustRegister(app, app.RootCmd, &migratecmd.Options{ 783 | TemplateLang: migratecmd.TemplateLangJS, // or migratecmd.TemplateLangGo (default) 784 | Dir: migrationsDir, 785 | Automigrate: true, 786 | }) 787 | ``` 788 | 789 | **The refactoring also comes with automigrations support.** 790 | 791 | If `Automigrate` is enabled (`true` by default for the prebuilt executable; can be disabled with `--automigrate=0`), 792 | PocketBase will generate seamlessly in the background JS (or Go) migration file with your collection changes. 793 | **The directory with the JS migrations can be committed to your git repo.** 794 | All migrations (Go and JS) are automatically executed on server start. 795 | Also note that the auto generated migrations are granural (in contrast to the `migrate collections` snapshot command) 796 | and allow multiple developers to do changes on the collections independently (even editing the same collection) miniziming the eventual merge conflicts. 797 | Here is a sample JS migration file that will be generated if you for example edit a single collection name: 798 | ```js 799 | // pb_migrations/1669663597_updated_posts_old.js 800 | migrate((db) => { 801 | // up 802 | const dao = new Dao(db) 803 | const collection = dao.findCollectionByNameOrId("lngf8rb3dqu86r3") 804 | collection.name = "posts_new" 805 | return dao.saveCollection(collection) 806 | }, (db) => { 807 | // down 808 | const dao = new Dao(db) 809 | const collection = dao.findCollectionByNameOrId("lngf8rb3dqu86r3") 810 | collection.name = "posts_old" 811 | return dao.saveCollection(collection) 812 | }) 813 | ``` 814 | 815 | - Added new `Dao` helpers to make it easier fetching and updating the app settings from a migration: 816 | ```go 817 | dao.FindSettings([optEncryptionKey]) 818 | dao.SaveSettings(newSettings, [optEncryptionKey]) 819 | ``` 820 | 821 | - Moved `core.Settings` to `models/settings.Settings`: 822 | ``` 823 | core.Settings{} -> settings.Settings{} 824 | core.NewSettings() -> settings.New() 825 | core.MetaConfig{} -> settings.MetaConfig{} 826 | core.LogsConfig{} -> settings.LogsConfig{} 827 | core.SmtpConfig{} -> settings.SmtpConfig{} 828 | core.S3Config{} -> settings.S3Config{} 829 | core.TokenConfig{} -> settings.TokenConfig{} 830 | core.AuthProviderConfig{} -> settings.AuthProviderConfig{} 831 | ``` 832 | 833 | - Changed the `mailer.Mailer` interface (**minor breaking if you are sending custom emails**): 834 | ```go 835 | // Old: 836 | app.NewMailClient().Send(from, to, subject, html, attachments?) 837 | 838 | // New: 839 | app.NewMailClient().Send(&mailer.Message{ 840 | From: from, 841 | To: to, 842 | Subject: subject, 843 | HTML: html, 844 | Attachments: attachments, 845 | // new configurable fields 846 | Bcc: []string{"bcc1@example.com", "bcc2@example.com"}, 847 | Cc: []string{"cc1@example.com", "cc2@example.com"}, 848 | Headers: map[string]string{"Custom-Header": "test"}, 849 | Text: "custom plain text version", 850 | }) 851 | ``` 852 | The new `*mailer.Message` struct is also now a member of the `MailerRecordEvent` and `MailerAdminEvent` events. 853 | 854 | - Other minor UI fixes and improvements 855 | 856 | 857 | ## v0.8.0 858 | 859 | **⚠️ This release contains breaking changes and requires some manual migration steps!** 860 | 861 | The biggest change is the merge of the `User` models and the `profiles` collection per [#376](https://github.com/pocketbase/pocketbase/issues/376). 862 | There is no longer `user` type field and the users are just an "auth" collection (we now support **collection types**, currently only "base" and "auth"). 863 | This should simplify the users management and at the same time allow us to have unlimited multiple "auth" collections each with their own custom fields and authentication options (eg. staff, client, etc.). 864 | 865 | In addition to the `Users` and `profiles` merge, this release comes with several other improvements: 866 | 867 | - Added indirect expand support [#312](https://github.com/pocketbase/pocketbase/issues/312#issuecomment-1242893496). 868 | 869 | - The `json` field type now supports filtering and sorting [#423](https://github.com/pocketbase/pocketbase/issues/423#issuecomment-1258302125). 870 | 871 | - The `relation` field now allows unlimited `maxSelect` (aka. without upper limit). 872 | 873 | - Added support for combined email/username + password authentication (see below `authWithPassword()`). 874 | 875 | - Added support for full _"manager-subordinate"_ users management, including a special API rule to allow directly changing system fields like email, password, etc. without requiring `oldPassword` or other user verification. 876 | 877 | - Enabled OAuth2 account linking on authorized request from the same auth collection (_this is useful for example if the OAuth2 provider doesn't return an email and you want to associate it with the current logged in user_). 878 | 879 | - Added option to toggle the record columns visibility from the table listing. 880 | 881 | - Added support for collection schema fields reordering. 882 | 883 | - Added several new OAuth2 providers (Microsoft Azure AD, Spotify, Twitch, Kakao). 884 | 885 | - Improved memory usage on large file uploads [#835](https://github.com/pocketbase/pocketbase/discussions/835). 886 | 887 | - More detailed API preview docs and site documentation (the repo is located at https://github.com/pocketbase/site). 888 | 889 | - Other minor performance improvements (mostly related to the search apis). 890 | 891 | ### Migrate from v0.7.x 892 | 893 | - **[Data](#data)** 894 | - **[SDKs](#sdks)** 895 | - **[API](#api)** 896 | - **[Internals](#internals)** 897 | 898 | #### Data 899 | 900 | The merge of users and profiles comes with several required db changes. 901 | The easiest way to apply them is to use the new temporary `upgrade` command: 902 | 903 | ```sh 904 | # make sure to have a copy of your pb_data in case something fails 905 | cp -r ./pb_data ./pb_data_backup 906 | 907 | # run the upgrade command 908 | ./pocketbase08 upgrade 909 | 910 | # start the application as usual 911 | ./pocketbase08 serve 912 | ``` 913 | 914 | The upgrade command: 915 | 916 | - Creates a new `users` collection with merged fields from the `_users` table and the `profiles` collection. 917 | The new user records will have the ids from the `profiles` collection. 918 | - Changes all `user` type fields to `relation` and update the references to point to the new user ids. 919 | - Renames all `@collection.profiles.*`, `@request.user.*` and `@request.user.profile.*` filters to `@collection.users.*` and `@request.auth.*`. 920 | - Appends `2` to all **schema field names** and **api filter rules** that conflicts with the new system reserved ones: 921 | ``` 922 | collectionId => collectionId2 923 | collectionName => collectionName2 924 | expand => expand2 925 | 926 | // only for the "profiles" collection fields: 927 | username => username2 928 | email => email2 929 | emailVisibility => emailVisibility2 930 | verified => verified2 931 | tokenKey => tokenKey2 932 | passwordHash => passwordHash2 933 | lastResetSentAt => lastResetSentAt2 934 | lastVerificationSentAt => lastVerificationSentAt2 935 | ``` 936 | 937 | #### SDKs 938 | 939 | Please check the individual SDK package changelog and apply the necessary changes in your code: 940 | 941 | - [**JavaScript SDK changelog**](https://github.com/pocketbase/js-sdk/blob/master/CHANGELOG.md) 942 | ```sh 943 | npm install pocketbase@latest --save 944 | ``` 945 | 946 | - [**Dart SDK changelog**](https://github.com/pocketbase/dart-sdk/blob/master/CHANGELOG.md) 947 | 948 | ```sh 949 | dart pub add pocketbase:^0.5.0 950 | # or with Flutter: 951 | flutter pub add pocketbase:^0.5.0 952 | ``` 953 | 954 | #### API 955 | 956 | > _**You don't have to read this if you are using an official SDK.**_ 957 | 958 | - The authorization schema is no longer necessary. Now it is auto detected from the JWT token payload: 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 |
OldNew
Authorization: Admin TOKENAuthorization: TOKEN
Authorization: User TOKENAuthorization: TOKEN
973 | 974 | - All datetime stings are now returned in ISO8601 format - with _Z_ suffix and space as separator between the date and time part: 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 |
OldNew
2022-01-02 03:04:05.6782022-01-02 03:04:05.678Z
985 | 986 | - Removed the `@` prefix from the system record fields for easier json parsing: 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 |
OldNew
@collectionIdcollectionId
@collectionNamecollectionName
@expandexpand
1005 | 1006 | - All users api handlers are moved under `/api/collections/:collection/`: 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1016 | 1019 | 1020 | 1021 | 1024 | 1027 | 1028 | 1029 | 1030 | 1037 | 1038 | 1039 | 1040 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 |
OldNew
1014 | GET /api/users/auth-methods 1015 | 1017 | GET /api/collections/:collection/auth-methods 1018 |
1022 | POST /api/users/refresh 1023 | 1025 | POST /api/collections/:collection/auth-refresh 1026 |
POST /api/users/auth-via-oauth2 1031 | POST /api/collections/:collection/auth-with-oauth2 1032 |
1033 | You can now also pass optional createData object on OAuth2 sign-up. 1034 |
1035 | Also please note that now required user/profile fields are properly validated when creating new auth model on OAuth2 sign-up. 1036 |
POST /api/users/auth-via-email 1041 | POST /api/collections/:collection/auth-with-password 1042 |
1043 | Handles username/email + password authentication. 1044 |
1045 | {"identity": "usernameOrEmail", "password": "123456"} 1046 |
POST /api/users/request-password-resetPOST /api/collections/:collection/request-password-reset
POST /api/users/confirm-password-resetPOST /api/collections/:collection/confirm-password-reset
POST /api/users/request-verificationPOST /api/collections/:collection/request-verification
POST /api/users/confirm-verificationPOST /api/collections/:collection/confirm-verification
POST /api/users/request-email-changePOST /api/collections/:collection/request-email-change
POST /api/users/confirm-email-changePOST /api/collections/:collection/confirm-email-change
GET /api/usersGET /api/collections/:collection/records
GET /api/users/:idGET /api/collections/:collection/records/:id
POST /api/usersPOST /api/collections/:collection/records
PATCH /api/users/:idPATCH /api/collections/:collection/records/:id
DELETE /api/users/:idDELETE /api/collections/:collection/records/:id
GET /api/users/:id/external-authsGET /api/collections/:collection/records/:id/external-auths
DELETE /api/users/:id/external-auths/:providerDELETE /api/collections/:collection/records/:id/external-auths/:provider
1101 | 1102 | _In relation to the above changes, the `user` property in the auth response is renamed to `record`._ 1103 | 1104 | - The admins api was also updated for consistency with the users api changes: 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1114 | 1117 | 1118 | 1119 | 1120 | 1127 | 1128 |
OldNew
1112 | POST /api/admins/refresh 1113 | 1115 | POST /api/admins/auth-refresh 1116 |
POST /api/admins/auth-via-email 1121 | POST /api/admins/auth-with-password 1122 |
1123 | {"identity": "test@example.com", "password": "123456"} 1124 |
1125 | (notice that the email body field was renamed to identity) 1126 |
1129 | 1130 | - To prevent confusion with the auth method responses, the following endpoints now returns 204 with empty body (previously 200 with token and auth model): 1131 | ``` 1132 | POST /api/admins/confirm-password-reset 1133 | POST /api/collections/:collection/confirm-password-reset 1134 | POST /api/collections/:collection/confirm-verification 1135 | POST /api/collections/:collection/confirm-email-change 1136 | ``` 1137 | 1138 | - Renamed the "user" related settings fields returned by `GET /api/settings`: 1139 | 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 |
OldNew
userAuthTokenrecordAuthToken
userPasswordResetTokenrecordPasswordResetToken
userEmailChangeTokenrecordEmailChangeToken
userVerificationTokenrecordVerificationToken
1161 | 1162 | #### Internals 1163 | 1164 | > _**You don't have to read this if you are not using PocketBase as framework.**_ 1165 | 1166 | - Removed `forms.New*WithConfig()` factories to minimize ambiguities. 1167 | If you need to pass a transaction Dao you can use the new `SetDao(dao)` method available to the form instances. 1168 | 1169 | - `forms.RecordUpsert.LoadData(data map[string]any)` now can bulk load external data from a map. 1170 | To load data from a request instance, you could use `forms.RecordUpsert.LoadRequest(r, optKeysPrefix = "")`. 1171 | 1172 | - `schema.RelationOptions.MaxSelect` has new type `*int` (_you can use the new `types.Pointer(123)` helper to assign pointer values_). 1173 | 1174 | - Renamed the constant `apis.ContextUserKey` (_"user"_) to `apis.ContextAuthRecordKey` (_"authRecord"_). 1175 | 1176 | - Replaced user related middlewares with their auth record alternative: 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1197 | 1198 |
OldNew
apis.RequireUserAuth()apis.RequireRecordAuth(optCollectionNames ...string)
apis.RequireAdminOrUserAuth()apis.RequireAdminOrRecordAuth(optCollectionNames ...string)
N/A 1193 | RequireSameContextRecordAuth() 1194 |
1195 | (requires the auth record to be from the same context collection) 1196 |
1199 | 1200 | - The following record Dao helpers now uses the collection id or name instead of `*models.Collection` instance to reduce the verbosity when fetching records: 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1225 | 1226 |
OldNew
dao.FindRecordById(collection, ...)dao.FindRecordById(collectionNameOrId, ...)
dao.FindRecordsByIds(collection, ...)dao.FindRecordsByIds(collectionNameOrId, ...)
dao.FindRecordsByExpr(collection, ...)dao.FindRecordsByExpr(collectionNameOrId, ...)
dao.FindFirstRecordByData(collection, ...)dao.FindFirstRecordByData(collectionNameOrId, ...)
dao.IsRecordValueUnique(collection, ...)dao.IsRecordValueUnique(collectionNameOrId, ...)
1227 | 1228 | - Replaced all User related Dao helpers with Record equivalents: 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 |
OldNew
dao.UserQuery()dao.RecordQuery(collection)
dao.FindUserById(id)dao.FindRecordById(collectionNameOrId, id)
dao.FindUserByToken(token, baseKey)dao.FindAuthRecordByToken(token, baseKey)
dao.FindUserByEmail(email)dao.FindAuthRecordByEmail(collectionNameOrId, email)
N/Adao.FindAuthRecordByUsername(collectionNameOrId, username)
1255 | 1256 | - Moved the formatted `ApiError` struct and factories to the `github.com/pocketbase/pocketbase/apis` subpackage: 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 |
OldNew
Import path
github.com/pocketbase/pocketbase/tools/restgithub.com/pocketbase/pocketbase/apis
Fields
rest.ApiError{}apis.ApiError{}
rest.NewNotFoundError()apis.NewNotFoundError()
rest.NewBadRequestError()apis.NewBadRequestError()
rest.NewForbiddenError()apis.NewForbiddenError()
rest.NewUnauthorizedError()apis.NewUnauthorizedError()
rest.NewApiError()apis.NewApiError()
1297 | 1298 | - Renamed `models.Record` helper getters: 1299 | 1300 | 1301 | 1302 | 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 | 1310 | 1311 | 1312 | 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | 1337 | 1338 | 1339 | 1340 |
OldNew
SetDataValueSet
GetDataValueGet
GetBoolDataValueGetBool
GetStringDataValueGetString
GetIntDataValueGetInt
GetFloatDataValueGetFloat
GetTimeDataValueGetTime
GetDateTimeDataValueGetDateTime
GetStringSliceDataValueGetStringSlice
1341 | 1342 | - Added new auth collection `models.Record` helpers: 1343 | ```go 1344 | func (m *Record) Username() string 1345 | func (m *Record) SetUsername(username string) error 1346 | func (m *Record) Email() string 1347 | func (m *Record) SetEmail(email string) error 1348 | func (m *Record) EmailVisibility() bool 1349 | func (m *Record) SetEmailVisibility(visible bool) error 1350 | func (m *Record) IgnoreEmailVisibility(state bool) 1351 | func (m *Record) Verified() bool 1352 | func (m *Record) SetVerified(verified bool) error 1353 | func (m *Record) TokenKey() string 1354 | func (m *Record) SetTokenKey(key string) error 1355 | func (m *Record) RefreshTokenKey() error 1356 | func (m *Record) LastResetSentAt() types.DateTime 1357 | func (m *Record) SetLastResetSentAt(dateTime types.DateTime) error 1358 | func (m *Record) LastVerificationSentAt() types.DateTime 1359 | func (m *Record) SetLastVerificationSentAt(dateTime types.DateTime) error 1360 | func (m *Record) ValidatePassword(password string) bool 1361 | func (m *Record) SetPassword(password string) error 1362 | ``` 1363 | 1364 | - Added option to return serialized custom `models.Record` fields data: 1365 | ```go 1366 | func (m *Record) UnknownData() map[string]any 1367 | func (m *Record) WithUnknownData(state bool) 1368 | ``` 1369 | 1370 | - Deleted `model.User`. Now the user data is stored as an auth `models.Record`. 1371 | 1372 | 1373 | 1374 | 1375 | 1376 | 1377 | 1378 | 1379 | 1380 | 1381 | 1382 | 1383 | 1384 | 1385 | 1386 | 1387 | 1388 | 1389 | 1390 | 1391 | 1392 | 1393 | 1394 | 1395 | 1396 | 1397 | 1398 | 1399 |
OldNew
User.EmailRecord.Email()
User.TokenKeyRecord.TokenKey()
User.VerifiedRecord.Verified()
User.SetPassword()Record.SetPassword()
User.RefreshTokenKey()Record.RefreshTokenKey()
etc.
1400 | 1401 | - Replaced `User` related event hooks with their `Record` alternative: 1402 | 1403 | 1404 | 1405 | 1406 | 1407 | 1408 | 1409 | 1410 | 1411 | 1412 | 1413 | 1414 | 1415 | 1416 | 1417 | 1418 | 1419 | 1420 | 1421 | 1422 | 1423 | 1424 | 1425 | 1426 | 1427 | 1428 | 1429 | 1430 | 1431 | 1432 | 1433 | 1434 | 1435 | 1436 | 1437 | 1438 | 1439 | 1440 | 1441 | 1442 | 1443 | 1444 | 1445 | 1446 | 1447 | 1448 | 1449 | 1450 | 1451 | 1452 | 1453 | 1454 | 1455 | 1456 | 1457 | 1458 | 1459 | 1460 | 1461 | 1462 | 1463 | 1464 | 1465 | 1466 | 1467 | 1468 | 1469 | 1470 | 1471 | 1472 | 1473 | 1474 | 1475 | 1476 | 1477 | 1478 | 1479 |
OldNew
OnMailerBeforeUserResetPasswordSend() *hook.Hook[*MailerUserEvent]OnMailerBeforeRecordResetPasswordSend() *hook.Hook[*MailerRecordEvent]
OnMailerAfterUserResetPasswordSend() *hook.Hook[*MailerUserEvent]OnMailerAfterRecordResetPasswordSend() *hook.Hook[*MailerRecordEvent]
OnMailerBeforeUserVerificationSend() *hook.Hook[*MailerUserEvent]OnMailerBeforeRecordVerificationSend() *hook.Hook[*MailerRecordEvent]
OnMailerAfterUserVerificationSend() *hook.Hook[*MailerUserEvent]OnMailerAfterRecordVerificationSend() *hook.Hook[*MailerRecordEvent]
OnMailerBeforeUserChangeEmailSend() *hook.Hook[*MailerUserEvent]OnMailerBeforeRecordChangeEmailSend() *hook.Hook[*MailerRecordEvent]
OnMailerAfterUserChangeEmailSend() *hook.Hook[*MailerUserEvent]OnMailerAfterRecordChangeEmailSend() *hook.Hook[*MailerRecordEvent]
OnUsersListRequest() *hook.Hook[*UserListEvent]OnRecordsListRequest() *hook.Hook[*RecordsListEvent]
OnUserViewRequest() *hook.Hook[*UserViewEvent]OnRecordViewRequest() *hook.Hook[*RecordViewEvent]
OnUserBeforeCreateRequest() *hook.Hook[*UserCreateEvent]OnRecordBeforeCreateRequest() *hook.Hook[*RecordCreateEvent]
OnUserAfterCreateRequest() *hook.Hook[*UserCreateEvent]OnRecordAfterCreateRequest() *hook.Hook[*RecordCreateEvent]
OnUserBeforeUpdateRequest() *hook.Hook[*UserUpdateEvent]OnRecordBeforeUpdateRequest() *hook.Hook[*RecordUpdateEvent]
OnUserAfterUpdateRequest() *hook.Hook[*UserUpdateEvent]OnRecordAfterUpdateRequest() *hook.Hook[*RecordUpdateEvent]
OnUserBeforeDeleteRequest() *hook.Hook[*UserDeleteEvent]OnRecordBeforeDeleteRequest() *hook.Hook[*RecordDeleteEvent]
OnUserAfterDeleteRequest() *hook.Hook[*UserDeleteEvent]OnRecordAfterDeleteRequest() *hook.Hook[*RecordDeleteEvent]
OnUserAuthRequest() *hook.Hook[*UserAuthEvent]OnRecordAuthRequest() *hook.Hook[*RecordAuthEvent]
OnUserListExternalAuths() *hook.Hook[*UserListExternalAuthsEvent]OnRecordListExternalAuths() *hook.Hook[*RecordListExternalAuthsEvent]
OnUserBeforeUnlinkExternalAuthRequest() *hook.Hook[*UserUnlinkExternalAuthEvent]OnRecordBeforeUnlinkExternalAuthRequest() *hook.Hook[*RecordUnlinkExternalAuthEvent]
OnUserAfterUnlinkExternalAuthRequest() *hook.Hook[*UserUnlinkExternalAuthEvent]OnRecordAfterUnlinkExternalAuthRequest() *hook.Hook[*RecordUnlinkExternalAuthEvent]
1480 | 1481 | - Replaced `forms.UserEmailLogin{}` with `forms.RecordPasswordLogin{}` (for both username and email depending on which is enabled for the collection). 1482 | 1483 | - Renamed user related `core.Settings` fields: 1484 | 1485 | 1486 | 1487 | 1488 | 1489 | 1490 | 1491 | 1492 | 1493 | 1494 | 1495 | 1496 | 1497 | 1498 | 1499 | 1500 | 1501 | 1502 | 1503 | 1504 | 1505 |
OldNew
core.Settings.UserAuthToken{}core.Settings.RecordAuthToken{}
core.Settings.UserPasswordResetToken{}core.Settings.RecordPasswordResetToken{}
core.Settings.UserEmailChangeToken{}core.Settings.RecordEmailChangeToken{}
core.Settings.UserVerificationToken{}core.Settings.RecordVerificationToken{}
1506 | 1507 | - Marked as "Deprecated" and will be removed in v0.9+: 1508 | ``` 1509 | core.Settings.EmailAuth{} 1510 | core.EmailAuthConfig{} 1511 | schema.FieldTypeUser 1512 | schema.UserOptions{} 1513 | ``` 1514 | 1515 | - The second argument of `apis.StaticDirectoryHandler(fileSystem, enableIndexFallback)` now is used to enable/disable index.html forwarding on missing file (eg. in case of SPA). 1516 | -------------------------------------------------------------------------------- /apps/backend/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2022 - present, Gani Georgiev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 7 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or 11 | substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 14 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 16 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /apps/backend/pb_data/data.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/apps/backend/pb_data/data.db -------------------------------------------------------------------------------- /apps/backend/pb_data/logs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/apps/backend/pb_data/logs.db -------------------------------------------------------------------------------- /apps/backend/pocketbase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/apps/backend/pocketbase -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/ambient.d.ts: -------------------------------------------------------------------------------- 1 | 2 | // this file is generated — do not edit it 3 | 4 | 5 | /// 6 | 7 | /** 8 | * Environment variables [loaded by Vite](https://vitejs.dev/guide/env-and-mode.html#env-files) from `.env` files and `process.env`. Like [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), this module cannot be imported into client-side code. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) _and do_ start with [`config.kit.env.privatePrefix`](https://kit.svelte.dev/docs/configuration#env) (if configured). 9 | * 10 | * _Unlike_ [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), the values exported from this module are statically injected into your bundle at build time, enabling optimisations like dead code elimination. 11 | * 12 | * ```ts 13 | * import { API_KEY } from '$env/static/private'; 14 | * ``` 15 | * 16 | * Note that all environment variables referenced in your code should be declared (for example in an `.env` file), even if they don't have a value until the app is deployed: 17 | * 18 | * ``` 19 | * MY_FEATURE_FLAG="" 20 | * ``` 21 | * 22 | * You can override `.env` values from the command line like so: 23 | * 24 | * ```bash 25 | * MY_FEATURE_FLAG="enabled" npm run dev 26 | * ``` 27 | */ 28 | declare module '$env/static/private' { 29 | export const NVM_INC: string; 30 | export const MANPATH: string; 31 | export const TERM_PROGRAM: string; 32 | export const NODE: string; 33 | export const INIT_CWD: string; 34 | export const NVM_CD_FLAGS: string; 35 | export const TERM: string; 36 | export const SHELL: string; 37 | export const npm_config_metrics_registry: string; 38 | export const HOMEBREW_REPOSITORY: string; 39 | export const TMPDIR: string; 40 | export const npm_config_global_prefix: string; 41 | export const TERM_PROGRAM_VERSION: string; 42 | export const ZDOTDIR: string; 43 | export const ORIGINAL_XDG_CURRENT_DESKTOP: string; 44 | export const MallocNanoZone: string; 45 | export const COLOR: string; 46 | export const npm_config_noproxy: string; 47 | export const npm_config_local_prefix: string; 48 | export const ZSH: string; 49 | export const NVM_DIR: string; 50 | export const USER: string; 51 | export const LS_COLORS: string; 52 | export const COMMAND_MODE: string; 53 | export const npm_config_globalconfig: string; 54 | export const SSH_AUTH_SOCK: string; 55 | export const __CF_USER_TEXT_ENCODING: string; 56 | export const npm_execpath: string; 57 | export const VIRTUAL_ENV_DISABLE_PROMPT: string; 58 | export const PAGER: string; 59 | export const LSCOLORS: string; 60 | export const PATH: string; 61 | export const npm_package_json: string; 62 | export const _: string; 63 | export const npm_config_userconfig: string; 64 | export const npm_config_init_module: string; 65 | export const USER_ZDOTDIR: string; 66 | export const __CFBundleIdentifier: string; 67 | export const npm_command: string; 68 | export const PWD: string; 69 | export const npm_lifecycle_event: string; 70 | export const EDITOR: string; 71 | export const npm_package_name: string; 72 | export const LANG: string; 73 | export const VSCODE_GIT_ASKPASS_EXTRA_ARGS: string; 74 | export const XPC_FLAGS: string; 75 | export const npm_config_node_gyp: string; 76 | export const npm_package_version: string; 77 | export const XPC_SERVICE_NAME: string; 78 | export const VSCODE_INJECTION: string; 79 | export const SHLVL: string; 80 | export const HOME: string; 81 | export const VSCODE_GIT_ASKPASS_MAIN: string; 82 | export const HOMEBREW_PREFIX: string; 83 | export const npm_config_cache: string; 84 | export const LESS: string; 85 | export const LOGNAME: string; 86 | export const npm_lifecycle_script: string; 87 | export const VSCODE_GIT_IPC_HANDLE: string; 88 | export const NVM_BIN: string; 89 | export const npm_config_user_agent: string; 90 | export const VSCODE_GIT_ASKPASS_NODE: string; 91 | export const GIT_ASKPASS: string; 92 | export const INFOPATH: string; 93 | export const HOMEBREW_CELLAR: string; 94 | export const npm_node_execpath: string; 95 | export const npm_config_prefix: string; 96 | export const COLORTERM: string; 97 | export const NODE_ENV: string; 98 | } 99 | 100 | /** 101 | * Similar to [`$env/static/private`](https://kit.svelte.dev/docs/modules#$env-static-private), except that it only includes environment variables that begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) (which defaults to `PUBLIC_`), and can therefore safely be exposed to client-side code. 102 | * 103 | * Values are replaced statically at build time. 104 | * 105 | * ```ts 106 | * import { PUBLIC_BASE_URL } from '$env/static/public'; 107 | * ``` 108 | */ 109 | declare module '$env/static/public' { 110 | 111 | } 112 | 113 | /** 114 | * This module provides access to runtime environment variables, as defined by the platform you're running on. For example if you're using [`adapter-node`](https://github.com/sveltejs/kit/tree/master/packages/adapter-node) (or running [`vite preview`](https://kit.svelte.dev/docs/cli)), this is equivalent to `process.env`. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) _and do_ start with [`config.kit.env.privatePrefix`](https://kit.svelte.dev/docs/configuration#env) (if configured). 115 | * 116 | * This module cannot be imported into client-side code. 117 | * 118 | * ```ts 119 | * import { env } from '$env/dynamic/private'; 120 | * console.log(env.DEPLOYMENT_SPECIFIC_VARIABLE); 121 | * ``` 122 | * 123 | * > In `dev`, `$env/dynamic` always includes environment variables from `.env`. In `prod`, this behavior will depend on your adapter. 124 | */ 125 | declare module '$env/dynamic/private' { 126 | export const env: { 127 | NVM_INC: string; 128 | MANPATH: string; 129 | TERM_PROGRAM: string; 130 | NODE: string; 131 | INIT_CWD: string; 132 | NVM_CD_FLAGS: string; 133 | TERM: string; 134 | SHELL: string; 135 | npm_config_metrics_registry: string; 136 | HOMEBREW_REPOSITORY: string; 137 | TMPDIR: string; 138 | npm_config_global_prefix: string; 139 | TERM_PROGRAM_VERSION: string; 140 | ZDOTDIR: string; 141 | ORIGINAL_XDG_CURRENT_DESKTOP: string; 142 | MallocNanoZone: string; 143 | COLOR: string; 144 | npm_config_noproxy: string; 145 | npm_config_local_prefix: string; 146 | ZSH: string; 147 | NVM_DIR: string; 148 | USER: string; 149 | LS_COLORS: string; 150 | COMMAND_MODE: string; 151 | npm_config_globalconfig: string; 152 | SSH_AUTH_SOCK: string; 153 | __CF_USER_TEXT_ENCODING: string; 154 | npm_execpath: string; 155 | VIRTUAL_ENV_DISABLE_PROMPT: string; 156 | PAGER: string; 157 | LSCOLORS: string; 158 | PATH: string; 159 | npm_package_json: string; 160 | _: string; 161 | npm_config_userconfig: string; 162 | npm_config_init_module: string; 163 | USER_ZDOTDIR: string; 164 | __CFBundleIdentifier: string; 165 | npm_command: string; 166 | PWD: string; 167 | npm_lifecycle_event: string; 168 | EDITOR: string; 169 | npm_package_name: string; 170 | LANG: string; 171 | VSCODE_GIT_ASKPASS_EXTRA_ARGS: string; 172 | XPC_FLAGS: string; 173 | npm_config_node_gyp: string; 174 | npm_package_version: string; 175 | XPC_SERVICE_NAME: string; 176 | VSCODE_INJECTION: string; 177 | SHLVL: string; 178 | HOME: string; 179 | VSCODE_GIT_ASKPASS_MAIN: string; 180 | HOMEBREW_PREFIX: string; 181 | npm_config_cache: string; 182 | LESS: string; 183 | LOGNAME: string; 184 | npm_lifecycle_script: string; 185 | VSCODE_GIT_IPC_HANDLE: string; 186 | NVM_BIN: string; 187 | npm_config_user_agent: string; 188 | VSCODE_GIT_ASKPASS_NODE: string; 189 | GIT_ASKPASS: string; 190 | INFOPATH: string; 191 | HOMEBREW_CELLAR: string; 192 | npm_node_execpath: string; 193 | npm_config_prefix: string; 194 | COLORTERM: string; 195 | NODE_ENV: string; 196 | [key: `PUBLIC_${string}`]: undefined; 197 | [key: `${string}`]: string | undefined; 198 | } 199 | } 200 | 201 | /** 202 | * Similar to [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), but only includes variables that begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) (which defaults to `PUBLIC_`), and can therefore safely be exposed to client-side code. 203 | * 204 | * Note that public dynamic environment variables must all be sent from the server to the client, causing larger network requests — when possible, use `$env/static/public` instead. 205 | * 206 | * ```ts 207 | * import { env } from '$env/dynamic/public'; 208 | * console.log(env.PUBLIC_DEPLOYMENT_SPECIFIC_VARIABLE); 209 | * ``` 210 | */ 211 | declare module '$env/dynamic/public' { 212 | export const env: { 213 | [key: `PUBLIC_${string}`]: string | undefined; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/generated/client/app.js: -------------------------------------------------------------------------------- 1 | export { matchers } from './matchers.js'; 2 | 3 | export const nodes = [ 4 | () => import('./nodes/0'), 5 | () => import('./nodes/1'), 6 | () => import('./nodes/2'), 7 | () => import('./nodes/3'), 8 | () => import('./nodes/4'), 9 | () => import('./nodes/5'), 10 | () => import('./nodes/6'), 11 | () => import('./nodes/7') 12 | ]; 13 | 14 | export const server_loads = [0]; 15 | 16 | export const dictionary = { 17 | "/": [2], 18 | "/cluster_profile": [~3], 19 | "/gui": [4], 20 | "/login": [~5], 21 | "/register": [~6], 22 | "/user_profile": [~7] 23 | }; 24 | 25 | export const hooks = { 26 | handleError: (({ error }) => { console.error(error) }), 27 | }; 28 | 29 | export { default as root } from '../root.svelte'; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/generated/client/matchers.js: -------------------------------------------------------------------------------- 1 | export const matchers = {}; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/generated/client/nodes/0.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../../src/routes/+layout.svelte"; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/generated/client/nodes/1.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../../node_modules/@sveltejs/kit/src/runtime/components/error.svelte"; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/generated/client/nodes/2.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../../src/routes/+page.svelte"; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/generated/client/nodes/3.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../../src/routes/cluster_profile/+page.svelte"; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/generated/client/nodes/4.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../../src/routes/gui/+page.svelte"; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/generated/client/nodes/5.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../../src/routes/login/+page.svelte"; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/generated/client/nodes/6.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../../src/routes/register/+page.svelte"; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/generated/client/nodes/7.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../../src/routes/user_profile/+page.svelte"; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/generated/root.svelte: -------------------------------------------------------------------------------- 1 | 2 | 39 | 40 | {#if constructors[1]} 41 | 42 | 43 | 44 | {:else} 45 | 46 | {/if} 47 | 48 | {#if mounted} 49 |
50 | {#if navigated} 51 | {title} 52 | {/if} 53 |
54 | {/if} -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/generated/server/internal.js: -------------------------------------------------------------------------------- 1 | 2 | import root from '../root.svelte'; 3 | import { set_building } from '__sveltekit/environment'; 4 | import { set_assets } from '__sveltekit/paths'; 5 | import { set_private_env, set_public_env } from '../../../node_modules/@sveltejs/kit/src/runtime/shared-server.js'; 6 | 7 | export const options = { 8 | app_template_contains_nonce: false, 9 | csp: {"mode":"auto","directives":{"upgrade-insecure-requests":false,"block-all-mixed-content":false},"reportOnly":{"upgrade-insecure-requests":false,"block-all-mixed-content":false}}, 10 | csrf_check_origin: true, 11 | track_server_fetches: false, 12 | embedded: false, 13 | env_public_prefix: 'PUBLIC_', 14 | env_private_prefix: '', 15 | hooks: null, // added lazily, via `get_hooks` 16 | preload_strategy: "modulepreload", 17 | root, 18 | service_worker: false, 19 | templates: { 20 | app: ({ head, body, assets, nonce, env }) => "\n\n\t\n\t\t\n\t\t\n\t\t\n\t\t" + head + "\n\t\n\t\n\t\t
" + body + "
\n\t\n\n", 21 | error: ({ status, message }) => "\n\n\t\n\t\t\n\t\t" + message + "\n\n\t\t\n\t\n\t\n\t\t
\n\t\t\t" + status + "\n\t\t\t
\n\t\t\t\t

" + message + "

\n\t\t\t
\n\t\t
\n\t\n\n" 22 | }, 23 | version_hash: "11p6vyx" 24 | }; 25 | 26 | export function get_hooks() { 27 | return import("../../../src/hooks.server.js"); 28 | } 29 | 30 | export { set_assets, set_building, set_private_env, set_public_env }; 31 | -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "$lib": [ 5 | "../src/lib" 6 | ], 7 | "$lib/*": [ 8 | "../src/lib/*" 9 | ] 10 | }, 11 | "rootDirs": [ 12 | "..", 13 | "./types" 14 | ], 15 | "importsNotUsedAsValues": "error", 16 | "isolatedModules": true, 17 | "preserveValueImports": true, 18 | "lib": [ 19 | "esnext", 20 | "DOM", 21 | "DOM.Iterable" 22 | ], 23 | "moduleResolution": "node", 24 | "module": "esnext", 25 | "target": "esnext", 26 | "ignoreDeprecations": "5.0" 27 | }, 28 | "include": [ 29 | "ambient.d.ts", 30 | "./types/**/$types.d.ts", 31 | "../vite.config.ts", 32 | "../src/**/*.js", 33 | "../src/**/*.ts", 34 | "../src/**/*.svelte", 35 | "../tests/**/*.js", 36 | "../tests/**/*.ts", 37 | "../tests/**/*.svelte" 38 | ], 39 | "exclude": [ 40 | "../node_modules/**", 41 | "./[!ambient.d.ts]**", 42 | "../src/service-worker.js", 43 | "../src/service-worker.ts", 44 | "../src/service-worker.d.ts" 45 | ] 46 | } -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/types/route_meta_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "/": [ 3 | "src/routes/+layout.server.js", 4 | "src/routes/+layout.server.js" 5 | ], 6 | "/cluster_profile": [ 7 | "src/routes/cluster_profile/+page.server.js", 8 | "src/routes/+layout.server.js" 9 | ], 10 | "/gui": [ 11 | "src/routes/+layout.server.js", 12 | "src/routes/gui/+server.js" 13 | ], 14 | "/login": [ 15 | "src/routes/login/+page.server.js", 16 | "src/routes/+layout.server.js" 17 | ], 18 | "/oauth": [ 19 | "src/routes/oauth/+server.js" 20 | ], 21 | "/register": [ 22 | "src/routes/register/+page.server.js", 23 | "src/routes/+layout.server.js" 24 | ], 25 | "/user_profile": [ 26 | "src/routes/user_profile/+page.server.js", 27 | "src/routes/+layout.server.js" 28 | ] 29 | } -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/types/src/routes/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | type RouteParams = { } 5 | type RouteId = '/'; 6 | type MaybeWithVoid = {} extends T ? T | void : T; 7 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 8 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 9 | type EnsureDefined = T extends null | undefined ? {} : T; 10 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 11 | export type Snapshot = Kit.Snapshot; 12 | type PageParentData = EnsureDefined; 13 | type LayoutRouteId = RouteId | "/" | "/cluster_profile" | "/gui" | "/login" | "/register" | "/user_profile" | null 14 | type LayoutParams = RouteParams & { } 15 | type LayoutServerParentData = EnsureDefined<{}>; 16 | type LayoutParentData = EnsureDefined<{}>; 17 | 18 | export type PageServerData = null; 19 | export type PageData = Expand; 20 | export type LayoutServerLoad = OutputDataShape> = Kit.ServerLoad; 21 | export type LayoutServerLoadEvent = Parameters[0]; 22 | export type LayoutServerData = Expand>>>>>; 23 | export type LayoutData = Expand & EnsureDefined>; 24 | export type RequestEvent = Kit.RequestEvent; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/types/src/routes/cluster_profile/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | type RouteParams = { } 5 | type RouteId = '/cluster_profile'; 6 | type MaybeWithVoid = {} extends T ? T | void : T; 7 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 8 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 9 | type EnsureDefined = T extends null | undefined ? {} : T; 10 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 11 | export type Snapshot = Kit.Snapshot; 12 | type PageServerParentData = EnsureDefined; 13 | type PageParentData = EnsureDefined; 14 | 15 | export type PageServerLoad = OutputDataShape> = Kit.ServerLoad; 16 | export type PageServerLoadEvent = Parameters[0]; 17 | export type ActionData = unknown; 18 | export type PageServerData = null; 19 | export type PageData = Expand; 20 | export type Action | void = Record | void> = Kit.Action 21 | export type Actions | void = Record | void> = Kit.Actions 22 | export type RequestEvent = Kit.RequestEvent; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/types/src/routes/gui/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | type RouteParams = { } 5 | type RouteId = '/gui'; 6 | type MaybeWithVoid = {} extends T ? T | void : T; 7 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 8 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 9 | type EnsureDefined = T extends null | undefined ? {} : T; 10 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 11 | export type Snapshot = Kit.Snapshot; 12 | type PageParentData = EnsureDefined; 13 | 14 | export type PageServerData = null; 15 | export type PageData = Expand; 16 | export type RequestHandler = Kit.RequestHandler; 17 | export type RequestEvent = Kit.RequestEvent; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/types/src/routes/login/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | type RouteParams = { } 5 | type RouteId = '/login'; 6 | type MaybeWithVoid = {} extends T ? T | void : T; 7 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 8 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 9 | type EnsureDefined = T extends null | undefined ? {} : T; 10 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 11 | export type Snapshot = Kit.Snapshot; 12 | type PageServerParentData = EnsureDefined; 13 | type PageParentData = EnsureDefined; 14 | 15 | export type PageServerLoad = OutputDataShape> = Kit.ServerLoad; 16 | export type PageServerLoadEvent = Parameters[0]; 17 | type ExcludeActionFailure = T extends Kit.ActionFailure ? never : T extends void ? never : T; 18 | type ActionsSuccess any>> = { [Key in keyof T]: ExcludeActionFailure>>; }[keyof T]; 19 | type ExtractActionFailure = T extends Kit.ActionFailure ? X extends void ? never : X : never; 20 | type ActionsFailure any>> = { [Key in keyof T]: Exclude>>, void>; }[keyof T]; 21 | type ActionsExport = typeof import('../../../../../src/routes/login/+page.server.js').actions 22 | export type SubmitFunction = Kit.SubmitFunction>, Expand>> 23 | export type ActionData = Expand> | null; 24 | export type PageServerData = null; 25 | export type PageData = Expand; 26 | export type Action | void = Record | void> = Kit.Action 27 | export type Actions | void = Record | void> = Kit.Actions 28 | export type RequestEvent = Kit.RequestEvent; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/types/src/routes/oauth/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | type RouteParams = { } 5 | type RouteId = '/oauth'; 6 | 7 | export type RequestHandler = Kit.RequestHandler; 8 | export type RequestEvent = Kit.RequestEvent; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/types/src/routes/register/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | type RouteParams = { } 5 | type RouteId = '/register'; 6 | type MaybeWithVoid = {} extends T ? T | void : T; 7 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 8 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 9 | type EnsureDefined = T extends null | undefined ? {} : T; 10 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 11 | export type Snapshot = Kit.Snapshot; 12 | type PageServerParentData = EnsureDefined; 13 | type PageParentData = EnsureDefined; 14 | 15 | export type PageServerLoad = OutputDataShape> = Kit.ServerLoad; 16 | export type PageServerLoadEvent = Parameters[0]; 17 | type ExcludeActionFailure = T extends Kit.ActionFailure ? never : T extends void ? never : T; 18 | type ActionsSuccess any>> = { [Key in keyof T]: ExcludeActionFailure>>; }[keyof T]; 19 | type ExtractActionFailure = T extends Kit.ActionFailure ? X extends void ? never : X : never; 20 | type ActionsFailure any>> = { [Key in keyof T]: Exclude>>, void>; }[keyof T]; 21 | type ActionsExport = typeof import('../../../../../src/routes/register/+page.server.js').actions 22 | export type SubmitFunction = Kit.SubmitFunction>, Expand>> 23 | export type ActionData = Expand> | null; 24 | export type PageServerData = null; 25 | export type PageData = Expand; 26 | export type Action | void = Record | void> = Kit.Action 27 | export type Actions | void = Record | void> = Kit.Actions 28 | export type RequestEvent = Kit.RequestEvent; -------------------------------------------------------------------------------- /apps/visualizer/.svelte-kit/types/src/routes/user_profile/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | type RouteParams = { } 5 | type RouteId = '/user_profile'; 6 | type MaybeWithVoid = {} extends T ? T | void : T; 7 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 8 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 9 | type EnsureDefined = T extends null | undefined ? {} : T; 10 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 11 | export type Snapshot = Kit.Snapshot; 12 | type PageServerParentData = EnsureDefined; 13 | type PageParentData = EnsureDefined; 14 | 15 | export type PageServerLoad = OutputDataShape> = Kit.ServerLoad; 16 | export type PageServerLoadEvent = Parameters[0]; 17 | export type ActionData = unknown; 18 | export type PageServerData = null; 19 | export type PageData = Expand; 20 | export type Action | void = Record | void> = Kit.Action 21 | export type Actions | void = Record | void> = Kit.Actions 22 | export type RequestEvent = Kit.RequestEvent; -------------------------------------------------------------------------------- /apps/visualizer/README.md: -------------------------------------------------------------------------------- 1 | # create-svelte 2 | 3 | Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte). 4 | 5 | ## Creating a project 6 | 7 | If you're seeing this, you've probably already done this step. Congrats! 8 | 9 | ```bash 10 | # create a new project in the current directory 11 | npm create svelte@latest 12 | 13 | # create a new project in my-app 14 | npm create svelte@latest my-app 15 | ``` 16 | 17 | ## Developing 18 | 19 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 20 | 21 | ```bash 22 | npm run dev 23 | 24 | # or start the server and open the app in a new browser tab 25 | npm run dev -- --open 26 | ``` 27 | 28 | ## Building 29 | 30 | To create a production version of your app: 31 | 32 | ```bash 33 | npm run build 34 | ``` 35 | 36 | You can preview the production build with `npm run preview`. 37 | 38 | > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. 39 | -------------------------------------------------------------------------------- /apps/visualizer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nysc-osp", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite dev", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "test": "npm run test:integration && npm run test:unit", 10 | "lint": "prettier --plugin-search-dir . --check . && eslint .", 11 | "format": "prettier --plugin-search-dir . --write .", 12 | "test:integration": "playwright test", 13 | "test:unit": "vitest" 14 | }, 15 | "devDependencies": { 16 | "@playwright/test": "^1.28.1", 17 | "@sveltejs/adapter-auto": "^2.0.0", 18 | "@sveltejs/kit": "^1.20.4", 19 | 20 | "autoprefixer": "^10.4.14", 21 | "daisyui": "^3.2.1", 22 | "eslint": "^8.28.0", 23 | "eslint-config-prettier": "^8.5.0", 24 | "eslint-plugin-svelte": "^2.30.0", 25 | "postcss": "^8.4.24", 26 | "postcss-load-config": "^4.0.1", 27 | "prettier": "^2.8.0", 28 | "prettier-plugin-svelte": "^2.10.1", 29 | "svelte": "^4.0.0", 30 | "svelte-check": "^3.4.3", 31 | "tailwindcss": "^3.3.2", 32 | "vite": "^4.3.6", 33 | "vitest": "^0.32.2" 34 | }, 35 | "type": "module", 36 | "dependencies": { 37 | "pocketbase": "^0.15.0-rc" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /apps/visualizer/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const tailwindcss = require("tailwindcss"); 2 | const autoprefixer = require("autoprefixer"); 3 | 4 | const config = { 5 | plugins: [ 6 | //Some plugins, like tailwindcss/nesting, need to run before Tailwind, 7 | tailwindcss(), 8 | //But others, like autoprefixer, need to run after, 9 | autoprefixer 10 | ] 11 | }; 12 | 13 | module.exports = config; -------------------------------------------------------------------------------- /apps/visualizer/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/visualizer/src/app.postcss: -------------------------------------------------------------------------------- 1 | /* Write your global styles here, in PostCSS syntax */ 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities -------------------------------------------------------------------------------- /apps/visualizer/src/hooks.server.js: -------------------------------------------------------------------------------- 1 | import PocketBase from 'pocketbase' 2 | import { serializeNonPOJOs } from '$lib/utils' 3 | 4 | // hook function - run on every request 5 | export const handle = async ({ event, resolve }) => { 6 | // new pocketbase instance 7 | event.locals.pb = new PocketBase('http://127.0.0.1:8090') 8 | // if we have a cookie from browser, grab it 9 | 10 | // event.locals.pb.authStore.loadFromCookie(event.request.headers.get('cookie') || '') 11 | // // if the user is valid 12 | // if (event.locals.pb.authStore.isValid) { 13 | // // when accessing user inside locals 14 | // event.locals.user = serializeNonPOJOs(event.locals.pb.authStore.model) 15 | // } else { 16 | // // if user is not valid 17 | // event.locals.user = undefined; 18 | // } 19 | 20 | const response = await resolve(event); 21 | // take all the info from current authstore and export into cookie, set into headers 22 | // response.headers.set('set-cookie', event.locals.pb.authStore.exportToCookie({ secure: false })); 23 | 24 | return response; 25 | } -------------------------------------------------------------------------------- /apps/visualizer/src/lib/utils.js: -------------------------------------------------------------------------------- 1 | // this is a page for utilities 2 | const { randomBytes } = await import('node:crypto') 3 | 4 | export const serializeNonPOJOs = (obj) => { 5 | // structured clone is a deep clone object, like JSON.stringify 6 | return structuredClone(obj) 7 | }; 8 | 9 | export const generateUsername = (name) => { 10 | const id = randomBytes(2).toString('hex'); 11 | return `${name.slice(0, 5)}${id}`; 12 | } -------------------------------------------------------------------------------- /apps/visualizer/src/routes/+layout.server.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | // nav bar 4 | // buttons that take you to different pages of the application 5 | // these buttons will change based on user actions (login, register, GUI, profile, cluster profile) 6 | // theme (styling) 7 | export const load = ({ locals }) => { 8 | if (locals.user) { 9 | return { 10 | user: locals.user 11 | }; 12 | }; 13 | 14 | return { 15 | user: undefined 16 | }; 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /apps/visualizer/src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 53 |
54 |
55 | 56 |
57 |
58 |
-------------------------------------------------------------------------------- /apps/visualizer/src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |

7 | Welcome to Kafkometry 8 |

9 |

10 | Please register if you 11 | do not have an account. 12 |

13 |

14 | Or sign in if you 15 | already an account. 16 |

17 | 36 | 37 | 51 |
52 | 53 | -------------------------------------------------------------------------------- /apps/visualizer/src/routes/cluster_profile/+page.server.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/apps/visualizer/src/routes/cluster_profile/+page.server.js -------------------------------------------------------------------------------- /apps/visualizer/src/routes/cluster_profile/+page.svelte: -------------------------------------------------------------------------------- 1 |
2 |

3 | Page for cluster profile 4 |

5 |

6 | Placeholder for cluster profile 7 |

8 |
-------------------------------------------------------------------------------- /apps/visualizer/src/routes/gui/+page.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /apps/visualizer/src/routes/gui/+server.js: -------------------------------------------------------------------------------- 1 | import { redirect } from "@sveltejs/kit" 2 | import { onDestroy } from "svelte"; 3 | import { invalidate } from "$app/navigation"; 4 | 5 | export const GET = async ({locals, url, cookies}) => { 6 | console.log(url.searchParams); 7 | 8 | const redirectURL = `${url.origin}/gui`; 9 | const expectedState = cookies.get('state'); 10 | const expectedVerifier = cookies.get('verifier'); 11 | 12 | console.log('expected state', expectedState); 13 | 14 | const state = await url.searchParams.get('state'); 15 | const code = await url.searchParams.get('code'); 16 | 17 | console.log('returned state', state); 18 | console.log('returned code', code); 19 | 20 | const authMethods = await locals.pb?.collection('users').listAuthMethods(); 21 | 22 | if (!authMethods?.authProviders) { 23 | console.log('No Auth Providers'); 24 | throw redirect(303,'/register'); 25 | } 26 | 27 | const provider = authMethods.authProviders[0]; 28 | 29 | if(!provider) { 30 | console.log('Provider Not Found'); 31 | throw redirect(303,'/register') 32 | } 33 | 34 | if (expectedState !== state) { 35 | console.log('Returned state does not match expected', expectedState, state); 36 | throw redirect(303,'/register') 37 | } 38 | 39 | try { 40 | await locals.pb?.collection('users').authWithOAuth2Code(provider.name, code, expectedVerifier, redirectURL, {name:'My User'}); 41 | } catch (err) { 42 | console.log('Error signing up with OAuth2', err) 43 | } 44 | 45 | throw redirect(303, '/') 46 | } 47 | 48 | const apiInterval = setInterval(async () => { 49 | await invalidate("/gui"); 50 | }, 1000000); 51 | 52 | onDestroy(() => { 53 | clearInterval(apiInterval); 54 | }); -------------------------------------------------------------------------------- /apps/visualizer/src/routes/login/+page.server.js: -------------------------------------------------------------------------------- 1 | import { error, redirect } from '@sveltejs/kit' 2 | // import { generateUsername } from '../../lib/utils.js' 3 | 4 | // this page will be for data/functionality of our register/login page 5 | export const actions = { 6 | // register: async ({ locals, request }) => { 7 | // const body = Object.fromEntries(await request.formData()) 8 | 9 | // let username = generateUsername(body.name.split(' ').join('')).toLocaleLowerCase() 10 | 11 | // try { 12 | // await locals.pb.collection('users').create({ username, ...body }) 13 | // await locals.pb.collection('users').requestVerification(body.email) 14 | // } 15 | // catch (err) { 16 | // console.log('Error: ', err) 17 | // throw error(500, 'Something went wrong') 18 | // } 19 | // throw redirect(303, '/login') 20 | // }, 21 | login: async ({ request, locals }) => { 22 | const body = Object.fromEntries(await request.formData()) 23 | 24 | try{ 25 | await locals.pb.collection('users').authWithPassword(body.email, body.password) 26 | if (!locals.pb?.authStore?.model?.verified) { 27 | locals.pb.authStore.clear() 28 | return { 29 | notVerified: true 30 | } 31 | } 32 | 33 | 34 | } catch (err) { 35 | console.log('Error: ', err); 36 | throw error(500, 'Something went wrong logging in'); 37 | } 38 | throw redirect(303, '/') 39 | }, 40 | 41 | OAuth2: async({ cookies, url, locals }) => { 42 | const authMethods = await locals.pb?.collection('users').listAuthMethods(); 43 | if(!authMethods){ 44 | return { 45 | authProviders: '', 46 | } 47 | } 48 | 49 | const redirectURL = `${url.origin}/gui` 50 | const googleAuthProvider = authMethods.authProviders[0]; 51 | const authProviderRedirect = `${googleAuthProvider.authUrl}${redirectURL}`; 52 | 53 | const state = googleAuthProvider.state; 54 | const verifier = googleAuthProvider.codeVerifier; 55 | cookies.set('state', state); 56 | cookies.set('verifier', verifier); 57 | 58 | throw redirect(302, authProviderRedirect) 59 | }, 60 | 61 | // CURRENTLY NOT WORKING 62 | // OAuth2Github: async({ cookies, url, locals }) => { 63 | // const authMethods = await locals.pb?.collection('users').listAuthMethods(); 64 | // if(!authMethods){ 65 | // return { 66 | // authProviders: '', 67 | // } 68 | // } 69 | 70 | // const redirectURL = `${url.origin}/gui` 71 | // const githubAuthProvider = authMethods.authProviders[1]; 72 | // const authProviderRedirect = `${githubAuthProvider.authUrl}${redirectURL}`; 73 | 74 | // const state = githubAuthProvider.state; 75 | // const verifier = githubAuthProvider.codeVerifier; 76 | // cookies.set('state', state); 77 | // cookies.set('verifier', verifier); 78 | 79 | // throw redirect(302, authProviderRedirect) 80 | // } 81 | 82 | } -------------------------------------------------------------------------------- /apps/visualizer/src/routes/login/+page.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |

7 | Login to your account 8 |

9 |

10 | Or register if you 11 | do not have an account. 12 |

13 |
14 | 15 |
16 | 19 | 20 |
21 |
22 | 25 | 26 |
27 | 28 |
29 | 30 |
31 |
32 | 33 |
34 |
35 |

Or

36 | 39 |
40 | {#if form?.notVerified} 41 |
42 | 43 | You must verify your email before logging in. 44 |
45 | {/if} 46 |
47 | 48 | 49 |
50 |
51 |

Or

52 | 55 |
56 | {#if form?.notVerified} 57 |
58 | 59 | You must verify your email before logging in. 60 |
61 | {/if} 62 |
63 |
-------------------------------------------------------------------------------- /apps/visualizer/src/routes/oauth/+server.js: -------------------------------------------------------------------------------- 1 | // import { redirect } from "@sveltejs/kit" 2 | 3 | // export const GET = async ({locals, url, cookies}) => { 4 | // console.log(url.searchParams); 5 | 6 | // const redirectURL = `${url.origin}/oauth`; 7 | // const expectedState = cookies.get('state'); 8 | // const expectedVerifier = cookies.get('verifier'); 9 | 10 | // console.log('expected state', expectedState); 11 | 12 | // const state = await url.searchParams.get('state'); 13 | // const code = await url.searchParams.get('code'); 14 | 15 | // console.log('returned state', state); 16 | // console.log('returned code', code); 17 | 18 | // const authMethods = await locals.pb?.collection('users').listAuthMethods(); 19 | 20 | // if (!authMethods?.authProviders) { 21 | // console.log('No Auth Providers'); 22 | // throw redirect(303,'/register'); 23 | // } 24 | 25 | // const provider = authMethods.authProviders[0]; 26 | 27 | // if(!provider) { 28 | // console.log('Provider Not Found'); 29 | // throw redirect(303,'/register') 30 | // } 31 | 32 | // if (expectedState !== state) { 33 | // console.log('Returned state does not match expected', expectedState, state); 34 | // throw redirect(303,'/register') 35 | // } 36 | 37 | // try { 38 | // await locals.pb?.collection('users').authWithOAuth2Code(provider.name, code, expectedVerifier, redirectURL, {name:'My User'}); 39 | // } catch (err) { 40 | // console.log('Error signing up with OAuth2', err) 41 | // } 42 | 43 | // throw redirect(303, '/') 44 | // } -------------------------------------------------------------------------------- /apps/visualizer/src/routes/register/+page.server.js: -------------------------------------------------------------------------------- 1 | import { error, redirect } from '@sveltejs/kit' 2 | import { generateUsername } from '../../lib/utils.js' 3 | 4 | // this page will be for data/functionality of our register/login page 5 | export const actions = { 6 | register: async ({ locals, request }) => { 7 | const body = Object.fromEntries(await request.formData()) 8 | 9 | let username = generateUsername(body.name.split(' ').join('')).toLocaleLowerCase() 10 | 11 | try { 12 | await locals.pb.collection('users').create({ username, ...body }) 13 | await locals.pb.collection('users').requestVerification(body.email) 14 | } 15 | catch (err) { 16 | console.log('Error: ', err) 17 | throw error(500, 'Something went wrong') 18 | } 19 | throw redirect(303, '/login') 20 | }, 21 | 22 | OAuth2: async({ cookies, url, locals }) => { 23 | const authMethods = await locals.pb?.collection('users').listAuthMethods(); 24 | if(!authMethods){ 25 | return { 26 | authProviders: '', 27 | } 28 | } 29 | 30 | const redirectURL = `${url.origin}/gui` 31 | const googleAuthProvider = authMethods.authProviders[0]; 32 | const authProviderRedirect = `${googleAuthProvider.authUrl}${redirectURL}`; 33 | 34 | const state = googleAuthProvider.state; 35 | const verifier = googleAuthProvider.codeVerifier; 36 | cookies.set('state', state); 37 | cookies.set('verifier', verifier); 38 | 39 | throw redirect(302, authProviderRedirect) 40 | } 41 | } -------------------------------------------------------------------------------- /apps/visualizer/src/routes/register/+page.svelte: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | Register for an account 5 |

6 |

7 | Or sign in if you 8 | already have an account. 9 |

10 |
11 |
12 | 15 | 16 |
17 |
18 | 21 | 22 |
23 |
24 | 27 | 28 |
29 |
30 | 33 | 34 |
35 |
36 | 37 |
38 |
39 | 40 | 41 |
42 | 43 | -------------------------------------------------------------------------------- /apps/visualizer/src/routes/user_profile/+page.server.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/apps/visualizer/src/routes/user_profile/+page.server.js -------------------------------------------------------------------------------- /apps/visualizer/src/routes/user_profile/+page.svelte: -------------------------------------------------------------------------------- 1 |
2 |

3 | Page for user profile 4 |

5 |

6 | Placeholder for user profile 7 |

8 |
-------------------------------------------------------------------------------- /apps/visualizer/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/apps/visualizer/static/favicon.png -------------------------------------------------------------------------------- /apps/visualizer/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto'; 2 | import { vitePreprocess } from '@sveltejs/kit/vite'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors 7 | // for more information about preprocessors 8 | preprocess: [vitePreprocess({})], 9 | 10 | kit: { 11 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. 12 | // If your environment is not supported or you settled on a specific environment, switch out the adapter. 13 | // See https://kit.svelte.dev/docs/adapters for more information about adapters. 14 | adapter: adapter() 15 | } 16 | }; 17 | 18 | export default config; 19 | -------------------------------------------------------------------------------- /apps/visualizer/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config}*/ 2 | const config = { 3 | content: ["./src/**/*.{html,js,svelte,ts}"], 4 | 5 | theme: { 6 | extend: {} 7 | }, 8 | daisyui: { 9 | themes: ["light", "dark", "cupcake"], 10 | }, 11 | 12 | plugins: [require("daisyui")] 13 | }; 14 | 15 | module.exports = config; -------------------------------------------------------------------------------- /apps/visualizer/tests/test.js: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@playwright/test'; 2 | 3 | test('index page has expected h1', async ({ page }) => { 4 | await page.goto('/'); 5 | await expect(page.getByRole('heading', { name: 'Welcome to SvelteKit' })).toBeVisible(); 6 | }); 7 | -------------------------------------------------------------------------------- /apps/visualizer/vite.config.js: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vite'; 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()] 6 | }); 7 | -------------------------------------------------------------------------------- /splash/assets/Kafkometry_Logo_Final.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /splash/assets/alwin.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/splash/assets/alwin.jpeg -------------------------------------------------------------------------------- /splash/assets/alwinAlt.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/splash/assets/alwinAlt.jpeg -------------------------------------------------------------------------------- /splash/assets/ben.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/splash/assets/ben.jpg -------------------------------------------------------------------------------- /splash/assets/dashboard.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/splash/assets/dashboard.gif -------------------------------------------------------------------------------- /splash/assets/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/splash/assets/dashboard.png -------------------------------------------------------------------------------- /splash/assets/mitch.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/splash/assets/mitch.jpeg -------------------------------------------------------------------------------- /splash/assets/vincent.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Kafkometry/2d1f3b5516aea3aae7447841b8304c053644765b/splash/assets/vincent.jpg -------------------------------------------------------------------------------- /splash/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 |
22 |
23 | 24 |
25 |
26 |
27 |
28 |
29 | 30 |

k a f k o m e t r y

31 |

A Smart Visualization Tool for Kafka

32 |



33 |
34 |
35 |
36 |
37 |
38 |

39 | Kafkometry is a lightweight, open-source visualizer for Kafka. With an emphasis on simplicity, Kafkometry is suited to engineers managing 40 | small-to-medium scale projects, or who are learning Kafka and want to fast-track their understanding of Kafka's data flow. 41 | Kafkometry is powered by Svelte that aims to visualize data from 42 | targeted clusters using Prometheus and Grafana. By using Svelte's in built features, data illustrations 43 | will be rendered for users at unbelieveable speeds! Your dashboards will only have the diagrams you want, 44 | clearing out all the distracting clutter! 45 |

46 |




47 |
48 | 51 |
52 |
53 |
54 |

The Kafkometry Dashboard

55 |


56 |
57 | Example screenshot of a dashboard we aim to make. 58 |
59 |
60 |
61 |
62 |
63 |
Meet the Team
64 |
65 |
66 |
67 |

Mitch Gruen

68 | 69 |
70 |
71 |
72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 |
81 |
82 |
83 |
84 |
85 |
86 |

Vincent Do

87 | 88 |
89 |
90 |
91 | 92 | 93 | 94 |
95 |
96 | 97 | 98 | 99 |
100 |
101 |
102 |
103 |
104 |
105 |

Alwin Zhao

106 | 107 |
108 |
109 |
110 | 111 | 112 | 113 |
114 |
115 | 116 | 117 | 118 |
119 |
120 |
121 |
122 |
123 |
124 |

Ben Dunn

125 | 126 |
127 |
128 |
129 | 130 | 131 | 132 |
133 |
134 | 135 | 136 | 137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |

146 | Powered By OSlabs |  147 | kafkometry@gmail.com 148 |

149 |
150 |
151 | 152 | 153 | -------------------------------------------------------------------------------- /splash/index.js: -------------------------------------------------------------------------------- 1 | addEventListener("DOMContentLoaded", (event) => { 2 | // console.log('test'); 3 | const [red, green, blue] = [255, 255, 255]; 4 | 5 | const container = document.getElementById("body"); 6 | container.style.background = "linear-gradient(135deg, rgb(255, 255, 255), rgb(38, 99, 194)"; 7 | 8 | const gitRedirect = document.getElementById('gitLogo') 9 | gitRedirect.style.opacity = 0; 10 | 11 | const kafkometryLogo = document.getElementById('kafkometryLogo') 12 | kafkometryLogo.style.opacity = 0; 13 | 14 | window.addEventListener("scroll", function () { 15 | let maxScrollValue = 16 | document.documentElement.scrollHeight - window.innerHeight; 17 | let currentScrollValue = document.documentElement.scrollTop; 18 | let y = currentScrollValue / maxScrollValue; 19 | // console.log(y); 20 | // console.log(y, gitRedirect.style.opacity) 21 | if (y > 0 && y < 0.4) { 22 | container.style.background = 23 | "linear-gradient(135deg, rgb(255, 255, 255), rgb(38, 99, 194)"; 24 | gitRedirect.style.opacity = y; 25 | // document.getElementById("gitLogo").style.filter="invert(0%)" 26 | kafkometryLogo.style.opacity = y; 27 | // container.style.backgroundColor = `rgb(${255 * (1 - y)}, ${255 * (1 - y)}, 255)`; 28 | } 29 | else if (y > 0.4 && y < 0.6) { 30 | let i = Math.min((y - 0.4) * 5, 1); 31 | container.style.background = `linear-gradient(135deg, rgb( 32 | ${255 - 255 * i + i * 17}, 33 | ${255 - 255 * i + i * 18}, 34 | ${255 - 255 * i + i * 24}), rgb( 35 | ${38 - 38 * i + i * 17}, 36 | ${99 - 99 * i + i * 18}, 37 | ${194 - 194 * i + i * 24})`; 38 | gitRedirect.style.opacity = 1-(2*y) 39 | kafkometryLogo.style.opacity = y; 40 | } else if(y>0.6 && y<0.8){ 41 | gitRedirect.style.opacity = 0; 42 | kafkometryLogo.style.opacity = y; 43 | document.getElementById("gitLogo").style.filter="invert(0%)" 44 | } else if (y > 0.8) { 45 | let i = Math.min((y - 0.8) * 5, 1); 46 | container.style.background = `linear-gradient(135deg, rgb( 47 | ${17 + 255 * i}, 48 | ${18 + 255 * i}, 49 | ${24 + 255 * i}), rgb( 50 | ${17 + 38 * i}, 51 | ${18 + 99 * i}, 52 | ${24 + 194 * i})`; 53 | gitRedirect.style.opacity = y; 54 | document.getElementById("gitLogo").style.filter="invert(100%)" 55 | kafkometryLogo.style.opacity = y; 56 | } 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /splash/style.css: -------------------------------------------------------------------------------- 1 | .body { 2 | /* background: linear-gradient(#FFFFFF,#5693F2); */ 3 | display: flex; 4 | justify-content: center; 5 | align-items: center; 6 | margin: 0px; 7 | } 8 | 9 | .center { 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | } 14 | 15 | .container { 16 | display: grid; 17 | /* grid-template-columns: repeat(6, 1fr); */ 18 | grid-template-columns: 1fr; 19 | grid-template-rows: repeat(4, 100vh); 20 | grid-template-areas: 21 | "pageArea1" 22 | "pageArea2" 23 | "pageArea3" 24 | "pageArea4"; 25 | justify-content: center; 26 | } 27 | 28 | .subContainer { 29 | width: min(100vw, 800px); 30 | } 31 | 32 | .pageArea1 { 33 | grid-area: pageArea1; 34 | display: flex; 35 | align-items: center; 36 | justify-content: center; 37 | /* background-color: greenyellow; */ 38 | } 39 | 40 | .pageArea2 { 41 | grid-area: pageArea2; 42 | display: flex; 43 | align-items: center; 44 | justify-content: center; 45 | /* background-color:red; */ 46 | } 47 | 48 | .pageArea3 { 49 | grid-area: pageArea3; 50 | display: flex; 51 | align-items: center; 52 | justify-content: center; 53 | /* background-color: purple; */ 54 | } 55 | 56 | .pageArea4 { 57 | grid-area: pageArea4; 58 | display: flex; 59 | align-items: center; 60 | justify-content: center; 61 | /* background-color: red; */ 62 | } 63 | 64 | h1 { 65 | /* display: flex; 66 | justify-content: center; 67 | align-items: center; */ 68 | display: flex; 69 | font-family: 'Raleway', sans-serif; 70 | font-size: 100px; 71 | color: #5693F2; 72 | margin: 0px; 73 | padding: 0px; 74 | /* text-shadow: 0px 30px 0px #5693F2; */ 75 | } 76 | 77 | h2 { 78 | display: flex; 79 | justify-content: center; 80 | align-items: center; 81 | font-family: 'Raleway', sans-serif; 82 | font-size: 30px; 83 | margin: 0px; 84 | padding: 0px; 85 | color: #5693F2; 86 | } 87 | 88 | h3 { 89 | display: flex; 90 | justify-content: center; 91 | align-items: center; 92 | font-family: 'Raleway', sans-serif; 93 | font-size: 50px; 94 | margin: 0px; 95 | padding: 0px; 96 | color: #5693F2; 97 | } 98 | 99 | p { 100 | display: flex; 101 | justify-content: center; 102 | align-items: center; 103 | font-family: 'Raleway', sans-serif; 104 | font-size: 15px; 105 | color: #444444; 106 | margin: 0px; 107 | padding: 0px; 108 | font-size: 28px; 109 | } 110 | 111 | .center { 112 | display: flex; 113 | justify-content: center; 114 | align-items: center; 115 | } 116 | 117 | #image { 118 | width: min(100vw, 1000px); 119 | box-shadow: 10px, 5px, 5px, green; 120 | } 121 | 122 | #devImage { 123 | width: 110px; 124 | } 125 | 126 | a { 127 | color: #FFFFFF; 128 | } 129 | 130 | a:hover { 131 | color: #CCCCCC; 132 | } 133 | 134 | .mitch:hover{ 135 | background-image: url('https://media.tenor.com/tWD3GjJcoHgAAAAC/spongebob-computer.gif'); 136 | } 137 | 138 | 139 | 140 | 141 | /* font-family: 'Lexend Deca', sans-serif; 142 | font-family: 'Outfit', sans-serif; */ -------------------------------------------------------------------------------- /splash/teamgrid.css: -------------------------------------------------------------------------------- 1 | .teamContainer { 2 | display: grid; 3 | /* grid-template-columns: repeat(6, 1fr); */ 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: 1; 6 | grid-template-areas: "member1 member2 member3 member4"; 7 | /* justify-content: center; */ 8 | max-width: 800px; 9 | } 10 | 11 | h4 { 12 | font-family: 'Raleway', sans-serif; 13 | color: white; 14 | font-size: 22px; 15 | margin-bottom: 8px; 16 | } 17 | 18 | .member1 { 19 | grid-area: member1; 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | /* background-color: greenyellow; */ 24 | } 25 | 26 | .member2 { 27 | grid-area: member2; 28 | display: flex; 29 | align-items: center; 30 | justify-content: center; 31 | /* background-color: red; */ 32 | } 33 | .member3 { 34 | grid-area: member3; 35 | display: flex; 36 | align-items: center; 37 | justify-content: center; 38 | /* background-color: yellow; */ 39 | } 40 | .member4 { 41 | grid-area: member4; 42 | display: flex; 43 | align-items: center; 44 | justify-content: center; 45 | /* background-color: blue; */ 46 | } 47 | 48 | .meetTheTeam { 49 | color: white; 50 | font-family: 'Raleway', sans-serif; 51 | font-size: 60px; 52 | display: flex; 53 | justify-content: center; 54 | } 55 | 56 | #teamImage { 57 | max-width: 120px; 58 | } 59 | 60 | .icon { 61 | margin: 7px 7px 7px 7px; 62 | color: white; 63 | font-size: 30px; 64 | } 65 | 66 | .icon:hover { 67 | color: #DDDDDD; 68 | } --------------------------------------------------------------------------------