├── .gitignore ├── Caddyfile ├── README.md ├── amber_profile_image.jpeg ├── appwrite.json ├── caddy.log ├── chapter.png ├── classic_profile_image.jpeg ├── cream_profile_image.jpeg ├── forest_profile_image.jpeg ├── functions ├── create-room │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── appwrite.js │ │ ├── livekit.js │ │ ├── main.js │ │ └── utils.js ├── database-cleaner │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── appwrite.js │ │ ├── main.js │ │ └── utils.js ├── delete-room │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── main.js │ │ └── utils.js ├── join-room │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── livekit.js │ │ ├── main.js │ │ └── utils.js ├── livekit-webhook │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── appwrite.js │ │ ├── livekit.js │ │ ├── main.js │ │ └── utils.js ├── match-maker │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── main.js │ │ └── utils.js ├── send-otp │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── appwrite.js │ │ ├── mail.js │ │ ├── main.js │ │ └── utils.js ├── upcomingRoom-isTime-checker │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ └── main.js ├── verify-email │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── main.js │ │ └── utils.js └── verify-otp │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ ├── main.js │ └── utils.js ├── init-auth.ps1 ├── init-auth.sh ├── init.ps1 ├── init.sh ├── package-lock.json ├── story.png ├── time_profile_image.jpeg └── vintage_profile_image.jpeg /.gitignore: -------------------------------------------------------------------------------- 1 | functions/upcomingRoom-isTime-checker/src/resonate-service-account.json 2 | .DS_Store 3 | livekit.log 4 | appwrite 5 | .vscode/ 6 | **/.env 7 | **/node_modules/ -------------------------------------------------------------------------------- /Caddyfile: -------------------------------------------------------------------------------- 1 | :5050 { 2 | # Handle LiveKit requests 3 | route /livekit/* { 4 | uri strip_prefix /livekit 5 | reverse_proxy http://host.docker.internal:7880 { 6 | header_up Host {host} 7 | header_up X-Real-IP {remote} 8 | header_up X-Forwarded-For {remote} 9 | header_up X-Forwarded-Proto {scheme} 10 | } 11 | } 12 | 13 | # Default site 14 | reverse_proxy /* http://host.docker.internal:80 { 15 | header_up Host {host} 16 | header_up X-Real-IP {remote} 17 | header_up X-Forwarded-For {remote} 18 | header_up X-Forwarded-Proto {scheme} 19 | } 20 | 21 | log { 22 | output file caddy_access.log 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Resonate - An Open Source Social Voice Platform 2 | 3 | ### This project is divided into two repositories: 4 | 5 | 1. [Resonate Flutter App](https://github.com/AOSSIE-Org/Resonate) 6 | 2. Resonate Backend (You are here) 7 | 8 | Go to [this repository](https://github.com/AOSSIE-Org/Resonate) to know more about the project. 9 | 10 | ## Environment Setup : 11 | 12 | 1. Pre-requisites : 13 | 14 | (a) Fork the Repo 15 | 16 | (b) Clone the Repo : `git clone https://github.com/AOSSIE-Org/Resonate-Backend` 17 | 18 | 2. Follow [this guide](https://docs.livekit.io/cloud/project-management/keys-and-tokens/) to obtain your `HOST`, `API-KEY`, `API-SECRET` from [livekit-cloud](https://livekit.io/cloud). 19 | 20 | 3. Create a new project on [Appwrite Cloud](https://appwrite.io/) or self host it locally by pulling their [docker image](https://appwrite.io/docs/self-hosting). Know more about Appwrite [here](https://appwrite.io/docs). 21 | 22 | ## Functions : 23 | 24 | (a) [Room Creation function](functions/create-room) : Function to create rooms in Appwrite and Livekit. 25 | 26 | (b) [Room Deletion function](functions/delete-room) : Function to remove rooms from Appwrite and Livekit. 27 | 28 | (c) [Room Joining function](functions/join-room) : Function to join room in Livekit. 29 | 30 | (d) [Livekit Webhook Receiver function](functions/livekit-webhook) : Function to receive webhooks from Livekit. 31 | 32 | (e) [Match Maker function](functions/match-maker) : Function to pair users for pair-chat feature. 33 | 34 | (f) [Send OTP function](functions/send-otp) : Function to send OTPs. 35 | 36 | (g) [Verify OTP function](functions/verify-otp) : Function to verify OTPs. 37 | 38 | (h) [Verify Email function](functions/verify-email) : Function to verify email ID of users. 39 | 40 | (i) [discussion-isTime-checker](functions/discussion-isTime-checker/) : A Cron Function to check all the existent Discussion scheduled timings and comparing to current time in order to activate a discussion if the current time is less than 5 minutes away from the scheduled time 41 | 42 | (j) [database-cleaner](functions/database-cleaner/) : Function to cleanup active pairs and participants collections in the database. 43 | 44 | ## Communication Channels 45 | 46 | If you have any questions, need clarifications, or want to discuss ideas, feel free to reach out through the following channels: 47 | 48 | - [Discord Server](https://discord.com/invite/6mFZ2S846n) 49 | - [Email](mailto:aossie.oss@gmail.com) 50 | 51 | We appreciate your contributions and look forward to working with you to make this project even better! 52 | -------------------------------------------------------------------------------- /amber_profile_image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOSSIE-Org/Resonate-Backend/8e2d387c45202d23f179ed224e8d3f58112e4c44/amber_profile_image.jpeg -------------------------------------------------------------------------------- /caddy.log: -------------------------------------------------------------------------------- 1 | {"level":"info","ts":1720522344.716894,"msg":"using adjacent Caddyfile"} 2 | {"level":"warn","ts":1720522344.717262,"logger":"caddyfile","msg":"Unnecessary header_up X-Forwarded-For: the reverse proxy's default behavior is to pass headers to the upstream"} 3 | {"level":"warn","ts":1720522344.71727,"logger":"caddyfile","msg":"Unnecessary header_up X-Forwarded-Proto: the reverse proxy's default behavior is to pass headers to the upstream"} 4 | {"level":"warn","ts":1720522344.717453,"logger":"caddyfile","msg":"Unnecessary header_up X-Forwarded-For: the reverse proxy's default behavior is to pass headers to the upstream"} 5 | {"level":"warn","ts":1720522344.71746,"logger":"caddyfile","msg":"Unnecessary header_up X-Forwarded-Proto: the reverse proxy's default behavior is to pass headers to the upstream"} 6 | {"level":"info","ts":1720522344.717768,"msg":"adapted config to JSON","adapter":"caddyfile"} 7 | {"level":"warn","ts":1720522344.717773,"msg":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"Caddyfile","line":2} 8 | {"level":"info","ts":1720522344.719521,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//[::1]:2019","//127.0.0.1:2019","//localhost:2019"]} 9 | {"level":"info","ts":1720522344.7196748,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0x14000359a80"} 10 | {"level":"info","ts":1720522344.7199922,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]} 11 | {"level":"info","ts":1720522344.7231958,"msg":"autosaved config (load with --resume flag)","file":"/Users/drnitashahasteer/Library/Application Support/Caddy/autosave.json"} 12 | {"level":"info","ts":1720522344.723207,"msg":"serving initial configuration"} 13 | {"level":"info","ts":1720522344.729147,"logger":"tls","msg":"storage cleaning happened too recently; skipping for now","storage":"FileStorage:/Users/drnitashahasteer/Library/Application Support/Caddy","instance":"bfd9b3b7-c62f-40f6-89bd-e2dc7bba30f8","try_again":1720608744.729142,"try_again_in":86399.999999667} 14 | {"level":"info","ts":1720522344.729276,"logger":"tls","msg":"finished cleaning storage units"} 15 | {"level":"info","ts":1720522878.718821,"logger":"admin.api","msg":"received request","method":"POST","host":"localhost:2019","uri":"/stop","remote_ip":"127.0.0.1","remote_port":"60482","headers":{"Accept-Encoding":["gzip"],"Content-Length":["0"],"Origin":["http://localhost:2019"],"User-Agent":["Go-http-client/1.1"]}} 16 | {"level":"warn","ts":1720522878.72018,"logger":"admin.api","msg":"exiting; byeee!! 👋"} 17 | {"level":"info","ts":1720522878.720444,"logger":"http","msg":"servers shutting down with eternal grace period"} 18 | {"level":"info","ts":1720522878.7232819,"logger":"admin","msg":"stopped previous server","address":"localhost:2019"} 19 | {"level":"info","ts":1720522878.723337,"logger":"admin.api","msg":"shutdown complete","exit_code":0} 20 | -------------------------------------------------------------------------------- /chapter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOSSIE-Org/Resonate-Backend/8e2d387c45202d23f179ed224e8d3f58112e4c44/chapter.png -------------------------------------------------------------------------------- /classic_profile_image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOSSIE-Org/Resonate-Backend/8e2d387c45202d23f179ed224e8d3f58112e4c44/classic_profile_image.jpeg -------------------------------------------------------------------------------- /cream_profile_image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOSSIE-Org/Resonate-Backend/8e2d387c45202d23f179ed224e8d3f58112e4c44/cream_profile_image.jpeg -------------------------------------------------------------------------------- /forest_profile_image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOSSIE-Org/Resonate-Backend/8e2d387c45202d23f179ed224e8d3f58112e4c44/forest_profile_image.jpeg -------------------------------------------------------------------------------- /functions/create-room/README.md: -------------------------------------------------------------------------------- 1 | # Room Creation function 2 | Function to create rooms in Appwrite and Livekit. 3 | 4 | ## 🧰 Usage 5 | 6 | ### POST / 7 | 8 | Receives new room data, and creates room in Livekit and corresponding room document in Appwrite. 9 | 10 | **Parameters** 11 | 12 | | Name | Description | Location | Type | Sample Value | 13 | | ------------- | ---------------------------------- | -------- | ------------------- | -------------------- | 14 | | name | Name of the room | Body | String | `sample_room` | 15 | | description | Room description | Body | String | `Sample description` | 16 | | adminUid | User ID of room admin | Body | String | `652000000002` | 17 | | tags | Array of room tags | Body | Array | `["sample-tag"]` | 18 | 19 | 20 | **Response** 21 | 22 | Sample `200` Response: 23 | 24 | ```json 25 | { 26 | "msg": "Room created Successfully", 27 | "livekit_room": "livekitRoom", 28 | "livekit_socket_url": "livekitSocketUrl", 29 | "access_token": "accessToken", 30 | } 31 | ``` 32 | 33 | Sample `400` Response: 34 | 35 | ```json 36 | { 37 | "msg": "Missing required parameter: name" 38 | } 39 | ``` 40 | 41 | Sample `500` Response: 42 | 43 | ```json 44 | { 45 | "msg": "Room Creation failed" 46 | } 47 | ``` 48 | 49 | ## ⚙️ Configuration 50 | 51 | | Setting | Value | 52 | | ----------------- | ------------------------------ | 53 | | Runtime | Node (18.0) | 54 | | Entrypoint | `src/main.js` | 55 | | Build Commands | `npm install && npm run start` | 56 | | Permissions | `any` | 57 | | Timeout (Seconds) | 15 | 58 | 59 | ## 🔒 Environment Variables 60 | 61 | ### APPWRITE_API_KEY 62 | 63 | API Key to use Appwrite Sever SDK. 64 | 65 | | Question | Answer | 66 | | ------------- | ------------------------------------------------------------------------------------------------------------------------ | 67 | | Required | Yes | 68 | | Sample Value | `62...97` | 69 | | Documentation | [Appwrite API Keys](https://appwrite.io/docs/advanced/platform/api-keys) | 70 | 71 | ### MASTER_DATABASE_ID 72 | 73 | Database ID of master database in appwrite. 74 | 75 | | Question | Answer | 76 | | ------------- | ------------------------------------------------------------------------------------------------------------------------ | 77 | | Required | Yes | 78 | | Sample Value | `Zjc...5PH` | 79 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 80 | 81 | ### ROOMS_COLLECTION_ID 82 | 83 | Collection ID of rooms collection. 84 | 85 | | Question | Answer | 86 | | ------------- | -------------------------------------------------------------------------------------------------------------- | 87 | | Required | Yes | 88 | | Sample Value | `NXOi3...IBHDa` | 89 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 90 | 91 | ### LIVEKIT_HOST 92 | 93 | Host URL of Livekit instance. 94 | 95 | | Question | Answer | 96 | | ------------- | -------------------------------------------------------------------------------------------------------------- | 97 | | Required | Yes | 98 | | Sample Value | `https://******.livekit.cloud` | 99 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | 100 | 101 | ### LIVEKIT_API_KEY 102 | 103 | API Key to use Livekit Server SDK. 104 | 105 | | Question | Answer | 106 | | ------------- | ----------------------------------------------------------------------------------------------------------------------------- | 107 | | Required | Yes | 108 | | Sample Value | `AP......9X` | 109 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | 110 | 111 | ### LIVEKIT_API_SECRET 112 | 113 | API Secret to use Livekit Server SDK. 114 | 115 | | Question | Answer | 116 | | ------------- | -------------------------------------------------------------------------------------------------------------- | 117 | | Required | Yes | 118 | | Sample Value | `HC1Itf...........dAAKF5o` | 119 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | 120 | 121 | ### LIVEKIT_SOCKET_URL 122 | 123 | Socket URL of Livekit instance. 124 | 125 | | Question | Answer | 126 | | ------------- | -------------------------------------------------------------------------------------------------------------- | 127 | | Required | Yes | 128 | | Sample Value | `wss://******.livekit.cloud` | 129 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | -------------------------------------------------------------------------------- /functions/create-room/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-room", 3 | "version": "1.0.0", 4 | "description": "Function to create room", 5 | "main": "src/main.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/main.js", 9 | "format": "prettier --write ." 10 | }, 11 | "dependencies": { 12 | "livekit-server-sdk": "^1.2.6", 13 | "node-appwrite": "^9.0.0" 14 | }, 15 | "devDependencies": { 16 | "prettier": "^3.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /functions/create-room/src/appwrite.js: -------------------------------------------------------------------------------- 1 | import { Client, Databases, ID } from "node-appwrite"; 2 | 3 | class AppwriteService { 4 | constructor() { 5 | const client = new Client(); 6 | client 7 | .setEndpoint( 8 | process.env.APPWRITE_ENDPOINT ?? "https://cloud.appwrite.io/v1" 9 | ) 10 | .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID) 11 | .setKey(process.env.APPWRITE_API_KEY); 12 | this.databases = new Databases(client); 13 | } 14 | 15 | async createRoom(newRoomData) { 16 | const newRoomDocRef = await this.databases.createDocument( 17 | process.env.MASTER_DATABASE_ID, 18 | process.env.ROOMS_COLLECTION_ID, 19 | ID.unique(), 20 | newRoomData 21 | ); 22 | 23 | return newRoomDocRef.$id; 24 | } 25 | } 26 | 27 | export default AppwriteService; 28 | -------------------------------------------------------------------------------- /functions/create-room/src/livekit.js: -------------------------------------------------------------------------------- 1 | import { AccessToken, RoomServiceClient } from "livekit-server-sdk"; 2 | 3 | class LivekitService { 4 | constructor() { 5 | this.roomServiceClient = new RoomServiceClient( 6 | `${process.env.LIVEKIT_HOST}`, 7 | `${process.env.LIVEKIT_API_KEY}`, 8 | `${process.env.LIVEKIT_API_SECRET}` 9 | ); 10 | } 11 | 12 | generateToken(roomName, participantUid, isRoomAdmin) { 13 | const accessToken = new AccessToken( 14 | `${process.env.LIVEKIT_API_KEY}`, 15 | `${process.env.LIVEKIT_API_SECRET}`, 16 | { 17 | identity: participantUid, 18 | } 19 | ); 20 | 21 | //adding permissions to the access token before converting to jwt 22 | accessToken.addGrant({ 23 | room: roomName, 24 | roomCreate: isRoomAdmin, 25 | roomAdmin: isRoomAdmin, 26 | roomJoin: true, 27 | canPublish: true, 28 | canPublishData: true, 29 | canSubscribe: true, 30 | roomList: true, 31 | }); 32 | 33 | //Return the JWT 34 | return accessToken.toJwt(); 35 | } 36 | 37 | async createRoom(roomOptions) { 38 | const newLivekitRoom = 39 | await this.roomServiceClient.createRoom(roomOptions); 40 | 41 | return newLivekitRoom; 42 | } 43 | } 44 | 45 | export default LivekitService; 46 | -------------------------------------------------------------------------------- /functions/create-room/src/main.js: -------------------------------------------------------------------------------- 1 | import AppwriteService from "./appwrite.js"; 2 | import LivekitService from "./livekit.js"; 3 | import { throwIfMissing } from "./utils.js"; 4 | 5 | export default async ({ req, res, log, error }) => { 6 | throwIfMissing(process.env, [ 7 | "APPWRITE_API_KEY", 8 | "MASTER_DATABASE_ID", 9 | "ROOMS_COLLECTION_ID", 10 | "LIVEKIT_HOST", 11 | "LIVEKIT_API_KEY", 12 | "LIVEKIT_API_SECRET", 13 | "LIVEKIT_SOCKET_URL", 14 | ]); 15 | 16 | const appwrite = new AppwriteService(); 17 | const livekit = new LivekitService(); 18 | 19 | try { 20 | throwIfMissing(JSON.parse(req.body), ["name", "adminUid", "tags"]); 21 | } catch (err) { 22 | error(err.message); 23 | return res.json({ msg: err.message }, 400); 24 | } 25 | 26 | try { 27 | log(req); 28 | const { name, description, adminUid, tags } = JSON.parse(req.body); 29 | 30 | // create a new room on appwrite 31 | const newRoomdata = { 32 | name, 33 | description, 34 | adminUid, 35 | tags, 36 | totalParticipants: 1, 37 | }; 38 | const appwriteRoomId = await appwrite.createRoom(newRoomdata); 39 | log(appwriteRoomId); 40 | 41 | // create a new livekit room 42 | const livekitRoomOptions = { 43 | name: appwriteRoomId, // using appwrite room doc id as livekit room name 44 | emptyTimeout: 300, // timeout in seconds 45 | }; 46 | const livekitRoom = await livekit.createRoom(livekitRoomOptions); 47 | log(livekitRoom); 48 | 49 | // Creating a token for the admin 50 | const accessToken = livekit.generateToken( 51 | appwriteRoomId, 52 | adminUid, 53 | true 54 | ); 55 | 56 | return res.json({ 57 | msg: "Room created Successfully", 58 | livekit_room: livekitRoom, 59 | livekit_socket_url: `${process.env.LIVEKIT_SOCKET_URL}`, 60 | access_token: accessToken, 61 | }); 62 | } catch (e) { 63 | error(String(e)); 64 | return res.json({ msg: "Room creation failed" }, 500); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /functions/create-room/src/utils.js: -------------------------------------------------------------------------------- 1 | export const throwIfMissing = (obj, keys) => { 2 | const missing = []; 3 | for (let key of keys) { 4 | if (!(key in obj) || !obj[key]) { 5 | missing.push(key); 6 | } 7 | } 8 | if (missing.length > 0) { 9 | throw new Error(`Missing required fields: ${missing.join(", ")}`); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /functions/database-cleaner/README.md: -------------------------------------------------------------------------------- 1 | Database Cleaner Function 2 | 3 | Function to cleanup active pairs and participants collections in the database. 4 | 5 | ## 🧰 Usage 6 | 7 | ### GET / 8 | 9 | Remove documents older than X days from the active_pairs collection and stale participants documents. 10 | 11 | **Response** 12 | 13 | Sample `200` Response: Database Cleanup completed 14 | 15 | ## ⚙️ Configuration 16 | 17 | | Setting | Value | 18 | | ----------------- | ------------------------------ | 19 | | Runtime | Node (18.0) | 20 | | Entrypoint | `src/main.js` | 21 | | Build Commands | `npm install && npm run start` | 22 | | Permissions | `any` | 23 | | CRON | `0 1 * * *` | 24 | | Timeout (Seconds) | 900 | 25 | 26 | ## 🔒 Environment Variables 27 | 28 | ### APPWRITE_API_KEY 29 | 30 | API Key to use Appwrite Sever SDK. 31 | 32 | | Question | Answer | 33 | | ------------- | ------------------------------------------------------------------------ | 34 | | Required | Yes | 35 | | Sample Value | `62...97` | 36 | | Documentation | [Appwrite API Keys](https://appwrite.io/docs/advanced/platform/api-keys) | 37 | 38 | ### MASTER_DATABASE_ID 39 | 40 | Database ID of master database in appwrite. 41 | 42 | | Question | Answer | 43 | | ------------- | --------------------------------------------------------------------------------------- | 44 | | Required | Yes | 45 | | Sample Value | `Zjc...5PH` | 46 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 47 | 48 | ### ROOMS_COLLECTION_ID 49 | 50 | Collection ID of rooms collection. 51 | 52 | | Question | Answer | 53 | | ------------- | --------------------------------------------------------------------------------------- | 54 | | Required | Yes | 55 | | Sample Value | `NXOi3...IBHDa` | 56 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 57 | 58 | ### PARTICIPANTS_COLLECTION_ID 59 | 60 | Collection ID of participants collection. 61 | 62 | | Question | Answer | 63 | | ------------- | --------------------------------------------------------------------------------------- | 64 | | Required | Yes | 65 | | Sample Value | `NXOi3...IBHDa` | 66 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 67 | 68 | ### ACTIVE_PAIRS_COLLECTION_ID 69 | 70 | Collection ID of active_pairs collection. 71 | 72 | | Question | Answer | 73 | | ------------- | --------------------------------------------------------------------------------------- | 74 | | Required | Yes | 75 | | Sample Value | `NXOi3...IBHDa` | 76 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 77 | 78 | ### RETENTION_PERIOD_DAYS 79 | 80 | The number of days you want to retain the active pair document. 81 | 82 | | Question | Answer | 83 | | ------------ | ------ | 84 | | Required | Yes | 85 | | Sample Value | `1` | 86 | -------------------------------------------------------------------------------- /functions/database-cleaner/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "database-cleaner", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "database-cleaner", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "node-appwrite": "^9.0.0" 12 | }, 13 | "devDependencies": { 14 | "prettier": "^3.0.0" 15 | } 16 | }, 17 | "node_modules/asynckit": { 18 | "version": "0.4.0", 19 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 20 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 21 | }, 22 | "node_modules/axios": { 23 | "version": "1.5.1", 24 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", 25 | "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", 26 | "dependencies": { 27 | "follow-redirects": "^1.15.0", 28 | "form-data": "^4.0.0", 29 | "proxy-from-env": "^1.1.0" 30 | } 31 | }, 32 | "node_modules/combined-stream": { 33 | "version": "1.0.8", 34 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 35 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 36 | "dependencies": { 37 | "delayed-stream": "~1.0.0" 38 | }, 39 | "engines": { 40 | "node": ">= 0.8" 41 | } 42 | }, 43 | "node_modules/delayed-stream": { 44 | "version": "1.0.0", 45 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 46 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 47 | "engines": { 48 | "node": ">=0.4.0" 49 | } 50 | }, 51 | "node_modules/follow-redirects": { 52 | "version": "1.15.3", 53 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", 54 | "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", 55 | "funding": [ 56 | { 57 | "type": "individual", 58 | "url": "https://github.com/sponsors/RubenVerborgh" 59 | } 60 | ], 61 | "engines": { 62 | "node": ">=4.0" 63 | }, 64 | "peerDependenciesMeta": { 65 | "debug": { 66 | "optional": true 67 | } 68 | } 69 | }, 70 | "node_modules/form-data": { 71 | "version": "4.0.0", 72 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 73 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 74 | "dependencies": { 75 | "asynckit": "^0.4.0", 76 | "combined-stream": "^1.0.8", 77 | "mime-types": "^2.1.12" 78 | }, 79 | "engines": { 80 | "node": ">= 6" 81 | } 82 | }, 83 | "node_modules/mime-db": { 84 | "version": "1.52.0", 85 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 86 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 87 | "engines": { 88 | "node": ">= 0.6" 89 | } 90 | }, 91 | "node_modules/mime-types": { 92 | "version": "2.1.35", 93 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 94 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 95 | "dependencies": { 96 | "mime-db": "1.52.0" 97 | }, 98 | "engines": { 99 | "node": ">= 0.6" 100 | } 101 | }, 102 | "node_modules/node-appwrite": { 103 | "version": "9.0.0", 104 | "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-9.0.0.tgz", 105 | "integrity": "sha512-iTcHbuaJfr6bP/HFkRVV+FcaumKkbINqZyypQdl+tYxv6Dx0bkB/YKUXGYfTkgP18TLPWQQB++OGQhi98dlo2w==", 106 | "dependencies": { 107 | "axios": "^1.3.6", 108 | "form-data": "^4.0.0" 109 | } 110 | }, 111 | "node_modules/prettier": { 112 | "version": "3.0.3", 113 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", 114 | "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", 115 | "dev": true, 116 | "bin": { 117 | "prettier": "bin/prettier.cjs" 118 | }, 119 | "engines": { 120 | "node": ">=14" 121 | }, 122 | "funding": { 123 | "url": "https://github.com/prettier/prettier?sponsor=1" 124 | } 125 | }, 126 | "node_modules/proxy-from-env": { 127 | "version": "1.1.0", 128 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 129 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /functions/database-cleaner/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "database-cleaner", 3 | "version": "1.0.0", 4 | "description": "Function to cleanup database", 5 | "main": "src/main.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/main.js", 9 | "format": "prettier --write ." 10 | }, 11 | "dependencies": { 12 | "node-appwrite": "^9.0.0" 13 | }, 14 | "devDependencies": { 15 | "prettier": "^3.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /functions/database-cleaner/src/appwrite.js: -------------------------------------------------------------------------------- 1 | import { Client, Databases, Query } from "node-appwrite"; 2 | import { getExpiryDate } from "./utils.js"; 3 | 4 | class AppwriteService { 5 | constructor() { 6 | const client = new Client(); 7 | client 8 | .setEndpoint( 9 | process.env.APPWRITE_ENDPOINT ?? "https://cloud.appwrite.io/v1" 10 | ) 11 | .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID) 12 | .setKey(process.env.APPWRITE_API_KEY); 13 | 14 | this.databases = new Databases(client); 15 | } 16 | 17 | async doesRoomExist(roomId) { 18 | try { 19 | await this.databases.getDocument( 20 | process.env.MASTER_DATABASE_ID, 21 | process.env.ROOMS_COLLECTION_ID, 22 | roomId 23 | ); 24 | return true; 25 | } catch (err) { 26 | if (err.code !== 404) throw err; 27 | return false; 28 | } 29 | } 30 | 31 | async cleanParticipantsCollection() { 32 | const participantDocs = await this.databases.listDocuments( 33 | process.env.MASTER_DATABASE_ID, 34 | process.env.PARTICIPANTS_COLLECTION_ID 35 | ); 36 | 37 | participantDocs.documents.forEach(async (participantDoc) => { 38 | if (!(await this.doesRoomExist(participantDoc.roomId))) { 39 | await this.databases.deleteDocument( 40 | process.env.MASTER_DATABASE_ID, 41 | process.env.PARTICIPANTS_COLLECTION_ID, 42 | participantDoc.$id 43 | ); 44 | } 45 | }); 46 | } 47 | 48 | async cleanActivePairsCollection() { 49 | let done = true; 50 | const queries = [ 51 | Query.lessThan("$createdAt", getExpiryDate()), 52 | Query.limit(25), 53 | ]; 54 | do { 55 | const activePairDocs = await this.databases.listDocuments( 56 | process.env.MASTER_DATABASE_ID, 57 | process.env.ACTIVE_PAIRS_COLLECTION_ID, 58 | queries 59 | ); 60 | await Promise.all( 61 | activePairDocs.documents.map((activePairDoc) => 62 | this.databases.deleteDocument( 63 | process.env.MASTER_DATABASE_ID, 64 | process.env.ACTIVE_PAIRS_COLLECTION_ID, 65 | activePairDoc.$id 66 | ) 67 | ) 68 | ); 69 | done = activePairDocs.total === 0; 70 | } while (!done); 71 | } 72 | 73 | 74 | 75 | // Clear all OTPs which are one day old 76 | async clearOldOTPs() { 77 | 78 | let done; 79 | 80 | const currentDate = new Date().toDateString(); 81 | 82 | const queries = [ 83 | Query.notEqual("date", currentDate), 84 | Query.limit(100), 85 | ]; 86 | 87 | // 88 | do { 89 | const oneDayOldOTPs = await this.databases.listDocuments( 90 | process.env.VERIFICATION_DATABASE_ID, 91 | process.env.OTP_COLLECTION_ID, 92 | queries 93 | ); 94 | 95 | await Promise.all( 96 | oneDayOldOTPs.documents.map(async (otp) => { 97 | await this.databases.deleteDocument( 98 | process.env.VERIFICATION_DATABASE_ID, 99 | process.env.OTP_COLLECTION_ID, 100 | otp.$id 101 | ); 102 | }) 103 | ) 104 | 105 | done = oneDayOldOTPs.total === 0; 106 | 107 | } while (!done); 108 | 109 | 110 | } 111 | } 112 | 113 | export default AppwriteService; 114 | -------------------------------------------------------------------------------- /functions/database-cleaner/src/main.js: -------------------------------------------------------------------------------- 1 | import AppwriteService from "./appwrite.js"; 2 | import { throwIfMissing } from "./utils.js"; 3 | 4 | export default async (context) => { 5 | throwIfMissing(process.env, [ 6 | "APPWRITE_API_KEY", 7 | "ROOMS_COLLECTION_ID", 8 | "PARTICIPANTS_COLLECTION_ID", 9 | "ACTIVE_PAIRS_COLLECTION_ID", 10 | "RETENTION_PERIOD_DAYS", 11 | "VERIFICATION_DATABASE_ID", 12 | "OTP_COLLECTION_ID", 13 | ]); 14 | 15 | const appwrite = new AppwriteService(); 16 | 17 | try { 18 | await appwrite.cleanParticipantsCollection(); 19 | } catch (e) { 20 | context.error(String(e)); 21 | } 22 | 23 | try { 24 | await appwrite.cleanActivePairsCollection(); 25 | } catch (e) { 26 | context.error(String(e)); 27 | } 28 | 29 | try { 30 | await appwrite.clearOldOTPs(); 31 | } catch (e) { 32 | context.error(String(e)); 33 | } 34 | 35 | return context.res.send("Database Cleanup completed. And unnecessary OTPs are also cleared."); 36 | }; 37 | -------------------------------------------------------------------------------- /functions/database-cleaner/src/utils.js: -------------------------------------------------------------------------------- 1 | export const throwIfMissing = (obj, keys) => { 2 | const missing = []; 3 | for (let key of keys) { 4 | if (!(key in obj) || (!obj[key] && obj[key] != 0)) { 5 | missing.push(key); 6 | } 7 | } 8 | if (missing.length > 0) { 9 | throw new Error(`Missing required fields: ${missing.join(", ")}`); 10 | } 11 | }; 12 | 13 | export const getExpiryDate = () => { 14 | const retentionPeriod = +(process.env.RETENTION_PERIOD_DAYS ?? 1); 15 | const expiryDate = new Date(); 16 | expiryDate.setDate(expiryDate.getDate() - retentionPeriod); 17 | return expiryDate.toISOString(); 18 | }; 19 | -------------------------------------------------------------------------------- /functions/delete-room/README.md: -------------------------------------------------------------------------------- 1 | # Room Deletion function 2 | Function to remove rooms from Appwrite and Livekit. 3 | 4 | ## 🧰 Usage 5 | 6 | ### POST / 7 | 8 | Receives ID of the room to be deleted, and removes the room from Appwrite. 9 | 10 | **Parameters** 11 | 12 | | Name | Description | Location | Type | Sample Value | 13 | | ------------- | ---------------------------------- | -------- | ------------------- | -------------------- | 14 | | appwriteRoomDocId | Document ID of the room | Body | String | `jcbd...kdsn` | 15 | 16 | 17 | **Response** 18 | 19 | Sample `200` Response: 20 | 21 | ```json 22 | { 23 | "msg": "Room deleted successfully" 24 | } 25 | ``` 26 | 27 | Sample `400` Response: 28 | 29 | ```json 30 | { 31 | "msg": "Missing required parameter: appwriteRoomDocId" 32 | } 33 | ``` 34 | 35 | Sample `500` Response: 36 | 37 | ```json 38 | { 39 | "msg": "Room deletion failed" 40 | } 41 | ``` 42 | 43 | ## ⚙️ Configuration 44 | 45 | | Setting | Value | 46 | | ----------------- | ------------------------------ | 47 | | Runtime | Node (18.0) | 48 | | Entrypoint | `src/main.js` | 49 | | Build Commands | `npm install && npm run start` | 50 | | Permissions | `Users` | 51 | | Timeout (Seconds) | 15 | 52 | 53 | ## 🔒 Environment Variables 54 | 55 | ### APPWRITE_API_KEY 56 | 57 | API Key to use Appwrite Sever SDK. 58 | 59 | | Question | Answer | 60 | | ------------- | ------------------------------------------------------------------------------------------------------------------------ | 61 | | Required | Yes | 62 | | Sample Value | `62...97` | 63 | | Documentation | [Appwrite API Keys](https://appwrite.io/docs/advanced/platform/api-keys) | 64 | 65 | ### MASTER_DATABASE_ID 66 | 67 | Database ID of master database in appwrite. 68 | 69 | | Question | Answer | 70 | | ------------- | ------------------------------------------------------------------------------------------------------------------------ | 71 | | Required | Yes | 72 | | Sample Value | `Zjc...5PH` | 73 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 74 | 75 | ### ROOMS_COLLECTION_ID 76 | 77 | Collection ID of rooms collection. 78 | 79 | | Question | Answer | 80 | | ------------- | -------------------------------------------------------------------------------------------------------------- | 81 | | Required | Yes | 82 | | Sample Value | `NXOi3...IBHDa` | 83 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 84 | 85 | ### PARTICIPANTS_COLLECTION_ID 86 | 87 | Collection ID of participants collection. 88 | 89 | | Question | Answer | 90 | | ------------- | -------------------------------------------------------------------------------------------------------------- | 91 | | Required | Yes | 92 | | Sample Value | `NXOi3...IBHDa` | 93 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 94 | 95 | ### LIVEKIT_HOST 96 | 97 | Host URL of Livekit instance. 98 | 99 | | Question | Answer | 100 | | ------------- | -------------------------------------------------------------------------------------------------------------- | 101 | | Required | Yes | 102 | | Sample Value | `https://******.livekit.cloud` | 103 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | 104 | 105 | ### LIVEKIT_API_KEY 106 | 107 | API Key to use Livekit Server SDK. 108 | 109 | | Question | Answer | 110 | | ------------- | ----------------------------------------------------------------------------------------------------------------------------- | 111 | | Required | Yes | 112 | | Sample Value | `AP......9X` | 113 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | 114 | 115 | ### LIVEKIT_API_SECRET 116 | 117 | API Secret to use Livekit Server SDK. 118 | 119 | | Question | Answer | 120 | | ------------- | -------------------------------------------------------------------------------------------------------------- | 121 | | Required | Yes | 122 | | Sample Value | `HC1Itf...........dAAKF5o` | 123 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | 124 | -------------------------------------------------------------------------------- /functions/delete-room/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "delete-room", 3 | "version": "1.0.0", 4 | "description": "Function to delete room", 5 | "main": "src/main.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/main.js", 9 | "format": "prettier --write ." 10 | }, 11 | "dependencies": { 12 | "livekit-server-sdk": "^1.2.6", 13 | "node-appwrite": "^9.0.0" 14 | }, 15 | "devDependencies": { 16 | "prettier": "^3.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /functions/delete-room/src/main.js: -------------------------------------------------------------------------------- 1 | import { Client, Databases, Query } from "node-appwrite"; 2 | import { RoomServiceClient } from "livekit-server-sdk"; 3 | import { throwIfMissing } from "./utils.js"; 4 | 5 | export default async ({ req, res, log, error }) => { 6 | throwIfMissing(process.env, [ 7 | "APPWRITE_API_KEY", 8 | "MASTER_DATABASE_ID", 9 | "ROOMS_COLLECTION_ID", 10 | "PARTICIPANTS_COLLECTION_ID", 11 | "LIVEKIT_HOST", 12 | "LIVEKIT_API_KEY", 13 | "LIVEKIT_API_SECRET", 14 | ]); 15 | 16 | const databases = new Databases( 17 | new Client() 18 | .setEndpoint( 19 | process.env.APPWRITE_ENDPOINT ?? "https://cloud.appwrite.io/v1" 20 | ) 21 | .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID) 22 | .setKey(process.env.APPWRITE_API_KEY) 23 | ); 24 | 25 | const roomServiceClient = new RoomServiceClient( 26 | `${process.env.LIVEKIT_HOST}`, 27 | `${process.env.LIVEKIT_API_KEY}`, 28 | `${process.env.LIVEKIT_API_SECRET}` 29 | ); 30 | 31 | try { 32 | throwIfMissing(JSON.parse(req.body), ["appwriteRoomDocId"]); 33 | } catch (err) { 34 | return res.json({ msg: err.message }, 400); 35 | } 36 | 37 | try { 38 | log(req); 39 | const { appwriteRoomDocId, livekitToken } = JSON.parse(req.body); 40 | 41 | const appwriteRoom = await databases.getDocument( 42 | process.env.MASTER_DATABASE_ID, 43 | process.env.ROOMS_COLLECTION_ID, 44 | appwriteRoomDocId 45 | ); 46 | 47 | const roomAdminUid = req.headers["x-appwrite-user-id"]; 48 | if (appwriteRoom.adminUid !== roomAdminUid) { 49 | log("User not room admin"); 50 | return res.json({ msg: "User is not room admin" }, 403); 51 | } 52 | 53 | //Delete Appwrite room doc 54 | await databases.deleteDocument( 55 | process.env.MASTER_DATABASE_ID, 56 | process.env.ROOMS_COLLECTION_ID, 57 | appwriteRoomDocId 58 | ); 59 | 60 | // Removing participants from collection 61 | const participantColRef = await databases.listDocuments( 62 | process.env.MASTER_DATABASE_ID, 63 | process.env.PARTICIPANTS_COLLECTION_ID, 64 | [Query.equal("roomId", [appwriteRoomDocId])] 65 | ); 66 | log(participantColRef); 67 | participantColRef.documents.forEach(async (participant) => { 68 | await databases.deleteDocument( 69 | process.env.MASTER_DATABASE_ID, 70 | process.env.PARTICIPANTS_COLLECTION_ID, 71 | participant.$id 72 | ); 73 | }); 74 | 75 | // Delete livekit room 76 | await roomServiceClient.deleteRoom(appwriteRoomDocId); 77 | return res.json({ msg: "Room deleted successfully" }); 78 | } catch (e) { 79 | error(String(e)); 80 | return res.json({ msg: "Room deletion failed" }, 500); 81 | } 82 | }; 83 | -------------------------------------------------------------------------------- /functions/delete-room/src/utils.js: -------------------------------------------------------------------------------- 1 | export const throwIfMissing = (obj, keys) => { 2 | const missing = []; 3 | for (let key of keys) { 4 | if (!(key in obj) || !obj[key]) { 5 | missing.push(key); 6 | } 7 | } 8 | if (missing.length > 0) { 9 | throw new Error(`Missing required fields: ${missing.join(", ")}`); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /functions/join-room/README.md: -------------------------------------------------------------------------------- 1 | # Room Joining function 2 | Function to join room in Livekit. 3 | 4 | ## 🧰 Usage 5 | 6 | ### POST / 7 | 8 | Receives IDs of the room and user, and sends back the necessary access to the user. 9 | 10 | **Parameters** 11 | 12 | | Name | Description | Location | Type | Sample Value | 13 | | ------------- | ---------------------------------- | -------- | ------------------- | -------------------- | 14 | | roomName | Name of the room | Body | String | `jcbd...kdsn` | 15 | | uid | User ID | Body | String | `dfge...ythr` | 16 | 17 | 18 | **Response** 19 | 20 | Sample `200` Response: 21 | 22 | ```json 23 | { 24 | "msg": "Success", 25 | "livekit_socket_url": "livekitSocketUrl", 26 | "access_token": "accessToken", 27 | } 28 | ``` 29 | 30 | Sample `400` Response: 31 | 32 | ```json 33 | { 34 | "msg": "Missing required parameter: roomName" 35 | } 36 | ``` 37 | 38 | Sample `500` Response: 39 | 40 | ```json 41 | { 42 | "msg": "Error joining room" 43 | } 44 | ``` 45 | 46 | ## ⚙️ Configuration 47 | 48 | | Setting | Value | 49 | | ----------------- | ------------------------------ | 50 | | Runtime | Node (18.0) | 51 | | Entrypoint | `src/main.js` | 52 | | Build Commands | `npm install && npm run start` | 53 | | Permissions | `Users` | 54 | | Timeout (Seconds) | 15 | 55 | 56 | ## 🔒 Environment Variables 57 | 58 | ### LIVEKIT_API_KEY 59 | 60 | API Key to use Livekit Server SDK. 61 | 62 | | Question | Answer | 63 | | ------------- | ----------------------------------------------------------------------------------------------------------------------------- | 64 | | Required | Yes | 65 | | Sample Value | `AP......9X` | 66 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | 67 | 68 | ### LIVEKIT_API_SECRET 69 | 70 | API Secret to use Livekit Server SDK. 71 | 72 | | Question | Answer | 73 | | ------------- | -------------------------------------------------------------------------------------------------------------- | 74 | | Required | Yes | 75 | | Sample Value | `HC1Itf...........dAAKF5o` | 76 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | 77 | 78 | ### LIVEKIT_SOCKET_URL 79 | 80 | Socket URL of Livekit instance. 81 | 82 | | Question | Answer | 83 | | ------------- | -------------------------------------------------------------------------------------------------------------- | 84 | | Required | Yes | 85 | | Sample Value | `wss://******.livekit.cloud` | 86 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | -------------------------------------------------------------------------------- /functions/join-room/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "join-room", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "join-room", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "livekit-server-sdk": "^1.2.6" 12 | }, 13 | "devDependencies": { 14 | "prettier": "^3.0.0" 15 | } 16 | }, 17 | "node_modules/@protobufjs/aspromise": { 18 | "version": "1.1.2", 19 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 20 | "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" 21 | }, 22 | "node_modules/@protobufjs/base64": { 23 | "version": "1.1.2", 24 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 25 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 26 | }, 27 | "node_modules/@protobufjs/codegen": { 28 | "version": "2.0.4", 29 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 30 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 31 | }, 32 | "node_modules/@protobufjs/eventemitter": { 33 | "version": "1.1.0", 34 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 35 | "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" 36 | }, 37 | "node_modules/@protobufjs/fetch": { 38 | "version": "1.1.0", 39 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 40 | "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", 41 | "dependencies": { 42 | "@protobufjs/aspromise": "^1.1.1", 43 | "@protobufjs/inquire": "^1.1.0" 44 | } 45 | }, 46 | "node_modules/@protobufjs/float": { 47 | "version": "1.0.2", 48 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 49 | "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" 50 | }, 51 | "node_modules/@protobufjs/inquire": { 52 | "version": "1.1.0", 53 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 54 | "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" 55 | }, 56 | "node_modules/@protobufjs/path": { 57 | "version": "1.1.2", 58 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 59 | "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" 60 | }, 61 | "node_modules/@protobufjs/pool": { 62 | "version": "1.1.0", 63 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 64 | "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" 65 | }, 66 | "node_modules/@protobufjs/utf8": { 67 | "version": "1.1.0", 68 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 69 | "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" 70 | }, 71 | "node_modules/@types/node": { 72 | "version": "20.8.2", 73 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz", 74 | "integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==" 75 | }, 76 | "node_modules/asynckit": { 77 | "version": "0.4.0", 78 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 79 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 80 | }, 81 | "node_modules/axios": { 82 | "version": "1.5.1", 83 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", 84 | "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", 85 | "dependencies": { 86 | "follow-redirects": "^1.15.0", 87 | "form-data": "^4.0.0", 88 | "proxy-from-env": "^1.1.0" 89 | } 90 | }, 91 | "node_modules/buffer-equal-constant-time": { 92 | "version": "1.0.1", 93 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 94 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" 95 | }, 96 | "node_modules/camelcase": { 97 | "version": "6.3.0", 98 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 99 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 100 | "engines": { 101 | "node": ">=10" 102 | }, 103 | "funding": { 104 | "url": "https://github.com/sponsors/sindresorhus" 105 | } 106 | }, 107 | "node_modules/camelcase-keys": { 108 | "version": "7.0.2", 109 | "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", 110 | "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", 111 | "dependencies": { 112 | "camelcase": "^6.3.0", 113 | "map-obj": "^4.1.0", 114 | "quick-lru": "^5.1.1", 115 | "type-fest": "^1.2.1" 116 | }, 117 | "engines": { 118 | "node": ">=12" 119 | }, 120 | "funding": { 121 | "url": "https://github.com/sponsors/sindresorhus" 122 | } 123 | }, 124 | "node_modules/combined-stream": { 125 | "version": "1.0.8", 126 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 127 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 128 | "dependencies": { 129 | "delayed-stream": "~1.0.0" 130 | }, 131 | "engines": { 132 | "node": ">= 0.8" 133 | } 134 | }, 135 | "node_modules/delayed-stream": { 136 | "version": "1.0.0", 137 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 138 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 139 | "engines": { 140 | "node": ">=0.4.0" 141 | } 142 | }, 143 | "node_modules/ecdsa-sig-formatter": { 144 | "version": "1.0.11", 145 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 146 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 147 | "dependencies": { 148 | "safe-buffer": "^5.0.1" 149 | } 150 | }, 151 | "node_modules/follow-redirects": { 152 | "version": "1.15.3", 153 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", 154 | "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", 155 | "funding": [ 156 | { 157 | "type": "individual", 158 | "url": "https://github.com/sponsors/RubenVerborgh" 159 | } 160 | ], 161 | "engines": { 162 | "node": ">=4.0" 163 | }, 164 | "peerDependenciesMeta": { 165 | "debug": { 166 | "optional": true 167 | } 168 | } 169 | }, 170 | "node_modules/form-data": { 171 | "version": "4.0.0", 172 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 173 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 174 | "dependencies": { 175 | "asynckit": "^0.4.0", 176 | "combined-stream": "^1.0.8", 177 | "mime-types": "^2.1.12" 178 | }, 179 | "engines": { 180 | "node": ">= 6" 181 | } 182 | }, 183 | "node_modules/jsonwebtoken": { 184 | "version": "9.0.2", 185 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", 186 | "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", 187 | "dependencies": { 188 | "jws": "^3.2.2", 189 | "lodash.includes": "^4.3.0", 190 | "lodash.isboolean": "^3.0.3", 191 | "lodash.isinteger": "^4.0.4", 192 | "lodash.isnumber": "^3.0.3", 193 | "lodash.isplainobject": "^4.0.6", 194 | "lodash.isstring": "^4.0.1", 195 | "lodash.once": "^4.0.0", 196 | "ms": "^2.1.1", 197 | "semver": "^7.5.4" 198 | }, 199 | "engines": { 200 | "node": ">=12", 201 | "npm": ">=6" 202 | } 203 | }, 204 | "node_modules/jwa": { 205 | "version": "1.4.1", 206 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", 207 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 208 | "dependencies": { 209 | "buffer-equal-constant-time": "1.0.1", 210 | "ecdsa-sig-formatter": "1.0.11", 211 | "safe-buffer": "^5.0.1" 212 | } 213 | }, 214 | "node_modules/jws": { 215 | "version": "3.2.2", 216 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 217 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 218 | "dependencies": { 219 | "jwa": "^1.4.1", 220 | "safe-buffer": "^5.0.1" 221 | } 222 | }, 223 | "node_modules/livekit-server-sdk": { 224 | "version": "1.2.6", 225 | "resolved": "https://registry.npmjs.org/livekit-server-sdk/-/livekit-server-sdk-1.2.6.tgz", 226 | "integrity": "sha512-Oc3lArzsyS3zzjQcTMqb3mnzHXTch0g8p4GqOcuaDaeo7J7vbf0soFUd94vURgqBq5h7wOTOOi7DlC2S0nj6LA==", 227 | "dependencies": { 228 | "axios": "^1.3.6", 229 | "camelcase-keys": "^7.0.0", 230 | "jsonwebtoken": "^9.0.0", 231 | "long": "^5.0.0", 232 | "protobufjs": "^7.2.4" 233 | } 234 | }, 235 | "node_modules/lodash.includes": { 236 | "version": "4.3.0", 237 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 238 | "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" 239 | }, 240 | "node_modules/lodash.isboolean": { 241 | "version": "3.0.3", 242 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 243 | "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" 244 | }, 245 | "node_modules/lodash.isinteger": { 246 | "version": "4.0.4", 247 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 248 | "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" 249 | }, 250 | "node_modules/lodash.isnumber": { 251 | "version": "3.0.3", 252 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 253 | "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" 254 | }, 255 | "node_modules/lodash.isplainobject": { 256 | "version": "4.0.6", 257 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 258 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" 259 | }, 260 | "node_modules/lodash.isstring": { 261 | "version": "4.0.1", 262 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 263 | "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" 264 | }, 265 | "node_modules/lodash.once": { 266 | "version": "4.1.1", 267 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 268 | "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" 269 | }, 270 | "node_modules/long": { 271 | "version": "5.2.3", 272 | "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", 273 | "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" 274 | }, 275 | "node_modules/lru-cache": { 276 | "version": "6.0.0", 277 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 278 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 279 | "dependencies": { 280 | "yallist": "^4.0.0" 281 | }, 282 | "engines": { 283 | "node": ">=10" 284 | } 285 | }, 286 | "node_modules/map-obj": { 287 | "version": "4.3.0", 288 | "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", 289 | "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", 290 | "engines": { 291 | "node": ">=8" 292 | }, 293 | "funding": { 294 | "url": "https://github.com/sponsors/sindresorhus" 295 | } 296 | }, 297 | "node_modules/mime-db": { 298 | "version": "1.52.0", 299 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 300 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 301 | "engines": { 302 | "node": ">= 0.6" 303 | } 304 | }, 305 | "node_modules/mime-types": { 306 | "version": "2.1.35", 307 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 308 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 309 | "dependencies": { 310 | "mime-db": "1.52.0" 311 | }, 312 | "engines": { 313 | "node": ">= 0.6" 314 | } 315 | }, 316 | "node_modules/ms": { 317 | "version": "2.1.3", 318 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 319 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 320 | }, 321 | "node_modules/prettier": { 322 | "version": "3.0.3", 323 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", 324 | "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", 325 | "dev": true, 326 | "bin": { 327 | "prettier": "bin/prettier.cjs" 328 | }, 329 | "engines": { 330 | "node": ">=14" 331 | }, 332 | "funding": { 333 | "url": "https://github.com/prettier/prettier?sponsor=1" 334 | } 335 | }, 336 | "node_modules/protobufjs": { 337 | "version": "7.2.5", 338 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", 339 | "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", 340 | "hasInstallScript": true, 341 | "dependencies": { 342 | "@protobufjs/aspromise": "^1.1.2", 343 | "@protobufjs/base64": "^1.1.2", 344 | "@protobufjs/codegen": "^2.0.4", 345 | "@protobufjs/eventemitter": "^1.1.0", 346 | "@protobufjs/fetch": "^1.1.0", 347 | "@protobufjs/float": "^1.0.2", 348 | "@protobufjs/inquire": "^1.1.0", 349 | "@protobufjs/path": "^1.1.2", 350 | "@protobufjs/pool": "^1.1.0", 351 | "@protobufjs/utf8": "^1.1.0", 352 | "@types/node": ">=13.7.0", 353 | "long": "^5.0.0" 354 | }, 355 | "engines": { 356 | "node": ">=12.0.0" 357 | } 358 | }, 359 | "node_modules/proxy-from-env": { 360 | "version": "1.1.0", 361 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 362 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 363 | }, 364 | "node_modules/quick-lru": { 365 | "version": "5.1.1", 366 | "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", 367 | "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", 368 | "engines": { 369 | "node": ">=10" 370 | }, 371 | "funding": { 372 | "url": "https://github.com/sponsors/sindresorhus" 373 | } 374 | }, 375 | "node_modules/safe-buffer": { 376 | "version": "5.2.1", 377 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 378 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 379 | "funding": [ 380 | { 381 | "type": "github", 382 | "url": "https://github.com/sponsors/feross" 383 | }, 384 | { 385 | "type": "patreon", 386 | "url": "https://www.patreon.com/feross" 387 | }, 388 | { 389 | "type": "consulting", 390 | "url": "https://feross.org/support" 391 | } 392 | ] 393 | }, 394 | "node_modules/semver": { 395 | "version": "7.5.4", 396 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", 397 | "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", 398 | "dependencies": { 399 | "lru-cache": "^6.0.0" 400 | }, 401 | "bin": { 402 | "semver": "bin/semver.js" 403 | }, 404 | "engines": { 405 | "node": ">=10" 406 | } 407 | }, 408 | "node_modules/type-fest": { 409 | "version": "1.4.0", 410 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", 411 | "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", 412 | "engines": { 413 | "node": ">=10" 414 | }, 415 | "funding": { 416 | "url": "https://github.com/sponsors/sindresorhus" 417 | } 418 | }, 419 | "node_modules/yallist": { 420 | "version": "4.0.0", 421 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 422 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 423 | } 424 | }, 425 | "dependencies": { 426 | "@protobufjs/aspromise": { 427 | "version": "1.1.2", 428 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 429 | "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" 430 | }, 431 | "@protobufjs/base64": { 432 | "version": "1.1.2", 433 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 434 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 435 | }, 436 | "@protobufjs/codegen": { 437 | "version": "2.0.4", 438 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 439 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 440 | }, 441 | "@protobufjs/eventemitter": { 442 | "version": "1.1.0", 443 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 444 | "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" 445 | }, 446 | "@protobufjs/fetch": { 447 | "version": "1.1.0", 448 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 449 | "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", 450 | "requires": { 451 | "@protobufjs/aspromise": "^1.1.1", 452 | "@protobufjs/inquire": "^1.1.0" 453 | } 454 | }, 455 | "@protobufjs/float": { 456 | "version": "1.0.2", 457 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 458 | "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" 459 | }, 460 | "@protobufjs/inquire": { 461 | "version": "1.1.0", 462 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 463 | "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" 464 | }, 465 | "@protobufjs/path": { 466 | "version": "1.1.2", 467 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 468 | "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" 469 | }, 470 | "@protobufjs/pool": { 471 | "version": "1.1.0", 472 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 473 | "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" 474 | }, 475 | "@protobufjs/utf8": { 476 | "version": "1.1.0", 477 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 478 | "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" 479 | }, 480 | "@types/node": { 481 | "version": "20.8.2", 482 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz", 483 | "integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==" 484 | }, 485 | "asynckit": { 486 | "version": "0.4.0", 487 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 488 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 489 | }, 490 | "axios": { 491 | "version": "1.5.1", 492 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", 493 | "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", 494 | "requires": { 495 | "follow-redirects": "^1.15.0", 496 | "form-data": "^4.0.0", 497 | "proxy-from-env": "^1.1.0" 498 | } 499 | }, 500 | "buffer-equal-constant-time": { 501 | "version": "1.0.1", 502 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 503 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" 504 | }, 505 | "camelcase": { 506 | "version": "6.3.0", 507 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 508 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" 509 | }, 510 | "camelcase-keys": { 511 | "version": "7.0.2", 512 | "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", 513 | "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", 514 | "requires": { 515 | "camelcase": "^6.3.0", 516 | "map-obj": "^4.1.0", 517 | "quick-lru": "^5.1.1", 518 | "type-fest": "^1.2.1" 519 | } 520 | }, 521 | "combined-stream": { 522 | "version": "1.0.8", 523 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 524 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 525 | "requires": { 526 | "delayed-stream": "~1.0.0" 527 | } 528 | }, 529 | "delayed-stream": { 530 | "version": "1.0.0", 531 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 532 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" 533 | }, 534 | "ecdsa-sig-formatter": { 535 | "version": "1.0.11", 536 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 537 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 538 | "requires": { 539 | "safe-buffer": "^5.0.1" 540 | } 541 | }, 542 | "follow-redirects": { 543 | "version": "1.15.3", 544 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", 545 | "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" 546 | }, 547 | "form-data": { 548 | "version": "4.0.0", 549 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 550 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 551 | "requires": { 552 | "asynckit": "^0.4.0", 553 | "combined-stream": "^1.0.8", 554 | "mime-types": "^2.1.12" 555 | } 556 | }, 557 | "jsonwebtoken": { 558 | "version": "9.0.2", 559 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", 560 | "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", 561 | "requires": { 562 | "jws": "^3.2.2", 563 | "lodash.includes": "^4.3.0", 564 | "lodash.isboolean": "^3.0.3", 565 | "lodash.isinteger": "^4.0.4", 566 | "lodash.isnumber": "^3.0.3", 567 | "lodash.isplainobject": "^4.0.6", 568 | "lodash.isstring": "^4.0.1", 569 | "lodash.once": "^4.0.0", 570 | "ms": "^2.1.1", 571 | "semver": "^7.5.4" 572 | } 573 | }, 574 | "jwa": { 575 | "version": "1.4.1", 576 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", 577 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 578 | "requires": { 579 | "buffer-equal-constant-time": "1.0.1", 580 | "ecdsa-sig-formatter": "1.0.11", 581 | "safe-buffer": "^5.0.1" 582 | } 583 | }, 584 | "jws": { 585 | "version": "3.2.2", 586 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 587 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 588 | "requires": { 589 | "jwa": "^1.4.1", 590 | "safe-buffer": "^5.0.1" 591 | } 592 | }, 593 | "livekit-server-sdk": { 594 | "version": "1.2.6", 595 | "resolved": "https://registry.npmjs.org/livekit-server-sdk/-/livekit-server-sdk-1.2.6.tgz", 596 | "integrity": "sha512-Oc3lArzsyS3zzjQcTMqb3mnzHXTch0g8p4GqOcuaDaeo7J7vbf0soFUd94vURgqBq5h7wOTOOi7DlC2S0nj6LA==", 597 | "requires": { 598 | "axios": "^1.3.6", 599 | "camelcase-keys": "^7.0.0", 600 | "jsonwebtoken": "^9.0.0", 601 | "long": "^5.0.0", 602 | "protobufjs": "^7.2.4" 603 | } 604 | }, 605 | "lodash.includes": { 606 | "version": "4.3.0", 607 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 608 | "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" 609 | }, 610 | "lodash.isboolean": { 611 | "version": "3.0.3", 612 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 613 | "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" 614 | }, 615 | "lodash.isinteger": { 616 | "version": "4.0.4", 617 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 618 | "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" 619 | }, 620 | "lodash.isnumber": { 621 | "version": "3.0.3", 622 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 623 | "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" 624 | }, 625 | "lodash.isplainobject": { 626 | "version": "4.0.6", 627 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 628 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" 629 | }, 630 | "lodash.isstring": { 631 | "version": "4.0.1", 632 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 633 | "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" 634 | }, 635 | "lodash.once": { 636 | "version": "4.1.1", 637 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 638 | "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" 639 | }, 640 | "long": { 641 | "version": "5.2.3", 642 | "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", 643 | "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" 644 | }, 645 | "lru-cache": { 646 | "version": "6.0.0", 647 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 648 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 649 | "requires": { 650 | "yallist": "^4.0.0" 651 | } 652 | }, 653 | "map-obj": { 654 | "version": "4.3.0", 655 | "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", 656 | "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==" 657 | }, 658 | "mime-db": { 659 | "version": "1.52.0", 660 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 661 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 662 | }, 663 | "mime-types": { 664 | "version": "2.1.35", 665 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 666 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 667 | "requires": { 668 | "mime-db": "1.52.0" 669 | } 670 | }, 671 | "ms": { 672 | "version": "2.1.3", 673 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 674 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 675 | }, 676 | "prettier": { 677 | "version": "3.0.3", 678 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", 679 | "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", 680 | "dev": true 681 | }, 682 | "protobufjs": { 683 | "version": "7.2.5", 684 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", 685 | "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", 686 | "requires": { 687 | "@protobufjs/aspromise": "^1.1.2", 688 | "@protobufjs/base64": "^1.1.2", 689 | "@protobufjs/codegen": "^2.0.4", 690 | "@protobufjs/eventemitter": "^1.1.0", 691 | "@protobufjs/fetch": "^1.1.0", 692 | "@protobufjs/float": "^1.0.2", 693 | "@protobufjs/inquire": "^1.1.0", 694 | "@protobufjs/path": "^1.1.2", 695 | "@protobufjs/pool": "^1.1.0", 696 | "@protobufjs/utf8": "^1.1.0", 697 | "@types/node": ">=13.7.0", 698 | "long": "^5.0.0" 699 | } 700 | }, 701 | "proxy-from-env": { 702 | "version": "1.1.0", 703 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 704 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 705 | }, 706 | "quick-lru": { 707 | "version": "5.1.1", 708 | "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", 709 | "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" 710 | }, 711 | "safe-buffer": { 712 | "version": "5.2.1", 713 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 714 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 715 | }, 716 | "semver": { 717 | "version": "7.5.4", 718 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", 719 | "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", 720 | "requires": { 721 | "lru-cache": "^6.0.0" 722 | } 723 | }, 724 | "type-fest": { 725 | "version": "1.4.0", 726 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", 727 | "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" 728 | }, 729 | "yallist": { 730 | "version": "4.0.0", 731 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 732 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 733 | } 734 | } 735 | } 736 | -------------------------------------------------------------------------------- /functions/join-room/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "join-room", 3 | "version": "1.0.0", 4 | "description": "Function to join room", 5 | "main": "src/main.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/main.js", 9 | "format": "prettier --write ." 10 | }, 11 | "dependencies": { 12 | "livekit-server-sdk": "^1.2.6" 13 | }, 14 | "devDependencies": { 15 | "prettier": "^3.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /functions/join-room/src/livekit.js: -------------------------------------------------------------------------------- 1 | import { AccessToken } from "livekit-server-sdk"; 2 | 3 | export const generateToken = (env, roomName, participantUid, isRoomAdmin) => { 4 | const accessToken = new AccessToken( 5 | env.LIVEKIT_API_KEY, 6 | env.LIVEKIT_API_SECRET, 7 | { 8 | identity: participantUid, 9 | } 10 | ); 11 | 12 | //adding permissions to the access token before converting to jwt 13 | accessToken.addGrant({ 14 | room: roomName, 15 | roomCreate: isRoomAdmin, 16 | roomAdmin: isRoomAdmin, 17 | roomJoin: true, 18 | canPublish: true, 19 | canPublishData: true, 20 | canSubscribe: true, 21 | roomList: true, 22 | }); 23 | 24 | //Return the JWT 25 | return accessToken.toJwt(); 26 | }; 27 | -------------------------------------------------------------------------------- /functions/join-room/src/main.js: -------------------------------------------------------------------------------- 1 | import { generateToken } from "./livekit.js"; 2 | import { throwIfMissing } from "./utils.js"; 3 | 4 | export default async ({ req, res, log, error }) => { 5 | throwIfMissing(process.env, [ 6 | "LIVEKIT_API_KEY", 7 | "LIVEKIT_API_SECRET", 8 | "LIVEKIT_SOCKET_URL", 9 | ]); 10 | 11 | try { 12 | throwIfMissing(JSON.parse(req.body), ["roomName", "uid"]); 13 | } catch (err) { 14 | return res.json({ msg: err.message }, 400); 15 | } 16 | 17 | try { 18 | log(req); 19 | const { roomName, uid: userId } = JSON.parse(req.body); 20 | 21 | const accessToken = generateToken(process.env, roomName, userId, false); 22 | 23 | return res.json({ 24 | msg: "Success", 25 | livekit_socket_url: `${process.env.LIVEKIT_SOCKET_URL}`, 26 | access_token: accessToken, 27 | }); 28 | } catch (e) { 29 | error(String(e)); 30 | return res.json({ msg: "Error joining room" }, 500); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /functions/join-room/src/utils.js: -------------------------------------------------------------------------------- 1 | export const throwIfMissing = (obj, keys) => { 2 | const missing = []; 3 | for (let key of keys) { 4 | if (!(key in obj) || !obj[key]) { 5 | missing.push(key); 6 | } 7 | } 8 | if (missing.length > 0) { 9 | throw new Error(`Missing required fields: ${missing.join(", ")}`); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /functions/livekit-webhook/README.md: -------------------------------------------------------------------------------- 1 | # Livekit Webhook Receiver function 2 | 3 | Function to receive webhooks from Livekit. 4 | 5 | ## ⚙️ Configuration 6 | 7 | | Setting | Value | 8 | | ----------------- | ------------------------------ | 9 | | Runtime | Node (18.0) | 10 | | Entrypoint | `src/main.js` | 11 | | Build Commands | `npm install && npm run start` | 12 | | Permissions | `any` | 13 | | Timeout (Seconds) | 15 | 14 | 15 | ## 🔒 Environment Variables 16 | 17 | ### APPWRITE_API_KEY 18 | 19 | API Key to use Appwrite Sever SDK. 20 | 21 | | Question | Answer | 22 | | ------------- | ------------------------------------------------------------------------ | 23 | | Required | Yes | 24 | | Sample Value | `62...97` | 25 | | Documentation | [Appwrite API Keys](https://appwrite.io/docs/advanced/platform/api-keys) | 26 | 27 | ### MASTER_DATABASE_ID 28 | 29 | Database ID of master database in appwrite. 30 | 31 | | Question | Answer | 32 | | ------------- | --------------------------------------------------------------------------------------- | 33 | | Required | Yes | 34 | | Sample Value | `Zjc...5PH` | 35 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 36 | 37 | ### ROOMS_COLLECTION_ID 38 | 39 | Collection ID of rooms collection. 40 | 41 | | Question | Answer | 42 | | ------------- | --------------------------------------------------------------------------------------- | 43 | | Required | Yes | 44 | | Sample Value | `NXOi3...IBHDa` | 45 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 46 | 47 | ### PARTICIPANTS_COLLECTION_ID 48 | 49 | Collection ID of participants collection. 50 | 51 | | Question | Answer | 52 | | ------------- | --------------------------------------------------------------------------------------- | 53 | | Required | Yes | 54 | | Sample Value | `NXOi3...IBHDa` | 55 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 56 | 57 | ### LIVEKIT_HOST 58 | 59 | Host URL of Livekit instance. 60 | 61 | | Question | Answer | 62 | | ------------- | -------------------------------------------- | 63 | | Required | Yes | 64 | | Sample Value | `https://******.livekit.cloud` | 65 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | 66 | 67 | ### LIVEKIT_API_KEY 68 | 69 | API Key to use Livekit Server SDK. 70 | 71 | | Question | Answer | 72 | | ------------- | -------------------------------------------- | 73 | | Required | Yes | 74 | | Sample Value | `AP......9X` | 75 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | 76 | 77 | ### LIVEKIT_API_SECRET 78 | 79 | API Secret to use Livekit Server SDK. 80 | 81 | | Question | Answer | 82 | | ------------- | -------------------------------------------- | 83 | | Required | Yes | 84 | | Sample Value | `HC1Itf...........dAAKF5o` | 85 | | Documentation | [Livekit](https://docs.livekit.io/realtime/) | 86 | -------------------------------------------------------------------------------- /functions/livekit-webhook/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-template", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "starter-template", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "livekit-server-sdk": "^1.2.6", 12 | "node-appwrite": "^9.0.0" 13 | }, 14 | "devDependencies": { 15 | "prettier": "^3.0.0" 16 | } 17 | }, 18 | "node_modules/@protobufjs/aspromise": { 19 | "version": "1.1.2", 20 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 21 | "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" 22 | }, 23 | "node_modules/@protobufjs/base64": { 24 | "version": "1.1.2", 25 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 26 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 27 | }, 28 | "node_modules/@protobufjs/codegen": { 29 | "version": "2.0.4", 30 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 31 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 32 | }, 33 | "node_modules/@protobufjs/eventemitter": { 34 | "version": "1.1.0", 35 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 36 | "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" 37 | }, 38 | "node_modules/@protobufjs/fetch": { 39 | "version": "1.1.0", 40 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 41 | "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", 42 | "dependencies": { 43 | "@protobufjs/aspromise": "^1.1.1", 44 | "@protobufjs/inquire": "^1.1.0" 45 | } 46 | }, 47 | "node_modules/@protobufjs/float": { 48 | "version": "1.0.2", 49 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 50 | "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" 51 | }, 52 | "node_modules/@protobufjs/inquire": { 53 | "version": "1.1.0", 54 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 55 | "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" 56 | }, 57 | "node_modules/@protobufjs/path": { 58 | "version": "1.1.2", 59 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 60 | "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" 61 | }, 62 | "node_modules/@protobufjs/pool": { 63 | "version": "1.1.0", 64 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 65 | "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" 66 | }, 67 | "node_modules/@protobufjs/utf8": { 68 | "version": "1.1.0", 69 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 70 | "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" 71 | }, 72 | "node_modules/@types/node": { 73 | "version": "20.7.0", 74 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.7.0.tgz", 75 | "integrity": "sha512-zI22/pJW2wUZOVyguFaUL1HABdmSVxpXrzIqkjsHmyUjNhPoWM1CKfvVuXfetHhIok4RY573cqS0mZ1SJEnoTg==" 76 | }, 77 | "node_modules/asynckit": { 78 | "version": "0.4.0", 79 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 80 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 81 | }, 82 | "node_modules/axios": { 83 | "version": "1.4.0", 84 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", 85 | "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", 86 | "dependencies": { 87 | "follow-redirects": "^1.15.0", 88 | "form-data": "^4.0.0", 89 | "proxy-from-env": "^1.1.0" 90 | } 91 | }, 92 | "node_modules/buffer-equal-constant-time": { 93 | "version": "1.0.1", 94 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 95 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" 96 | }, 97 | "node_modules/camelcase": { 98 | "version": "6.3.0", 99 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 100 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 101 | "engines": { 102 | "node": ">=10" 103 | }, 104 | "funding": { 105 | "url": "https://github.com/sponsors/sindresorhus" 106 | } 107 | }, 108 | "node_modules/camelcase-keys": { 109 | "version": "7.0.2", 110 | "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", 111 | "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", 112 | "dependencies": { 113 | "camelcase": "^6.3.0", 114 | "map-obj": "^4.1.0", 115 | "quick-lru": "^5.1.1", 116 | "type-fest": "^1.2.1" 117 | }, 118 | "engines": { 119 | "node": ">=12" 120 | }, 121 | "funding": { 122 | "url": "https://github.com/sponsors/sindresorhus" 123 | } 124 | }, 125 | "node_modules/combined-stream": { 126 | "version": "1.0.8", 127 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 128 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 129 | "dependencies": { 130 | "delayed-stream": "~1.0.0" 131 | }, 132 | "engines": { 133 | "node": ">= 0.8" 134 | } 135 | }, 136 | "node_modules/delayed-stream": { 137 | "version": "1.0.0", 138 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 139 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 140 | "engines": { 141 | "node": ">=0.4.0" 142 | } 143 | }, 144 | "node_modules/ecdsa-sig-formatter": { 145 | "version": "1.0.11", 146 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 147 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 148 | "dependencies": { 149 | "safe-buffer": "^5.0.1" 150 | } 151 | }, 152 | "node_modules/follow-redirects": { 153 | "version": "1.15.2", 154 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 155 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 156 | "funding": [ 157 | { 158 | "type": "individual", 159 | "url": "https://github.com/sponsors/RubenVerborgh" 160 | } 161 | ], 162 | "engines": { 163 | "node": ">=4.0" 164 | }, 165 | "peerDependenciesMeta": { 166 | "debug": { 167 | "optional": true 168 | } 169 | } 170 | }, 171 | "node_modules/form-data": { 172 | "version": "4.0.0", 173 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 174 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 175 | "dependencies": { 176 | "asynckit": "^0.4.0", 177 | "combined-stream": "^1.0.8", 178 | "mime-types": "^2.1.12" 179 | }, 180 | "engines": { 181 | "node": ">= 6" 182 | } 183 | }, 184 | "node_modules/jsonwebtoken": { 185 | "version": "9.0.2", 186 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", 187 | "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", 188 | "dependencies": { 189 | "jws": "^3.2.2", 190 | "lodash.includes": "^4.3.0", 191 | "lodash.isboolean": "^3.0.3", 192 | "lodash.isinteger": "^4.0.4", 193 | "lodash.isnumber": "^3.0.3", 194 | "lodash.isplainobject": "^4.0.6", 195 | "lodash.isstring": "^4.0.1", 196 | "lodash.once": "^4.0.0", 197 | "ms": "^2.1.1", 198 | "semver": "^7.5.4" 199 | }, 200 | "engines": { 201 | "node": ">=12", 202 | "npm": ">=6" 203 | } 204 | }, 205 | "node_modules/jwa": { 206 | "version": "1.4.1", 207 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", 208 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 209 | "dependencies": { 210 | "buffer-equal-constant-time": "1.0.1", 211 | "ecdsa-sig-formatter": "1.0.11", 212 | "safe-buffer": "^5.0.1" 213 | } 214 | }, 215 | "node_modules/jws": { 216 | "version": "3.2.2", 217 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 218 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 219 | "dependencies": { 220 | "jwa": "^1.4.1", 221 | "safe-buffer": "^5.0.1" 222 | } 223 | }, 224 | "node_modules/livekit-server-sdk": { 225 | "version": "1.2.6", 226 | "resolved": "https://registry.npmjs.org/livekit-server-sdk/-/livekit-server-sdk-1.2.6.tgz", 227 | "integrity": "sha512-Oc3lArzsyS3zzjQcTMqb3mnzHXTch0g8p4GqOcuaDaeo7J7vbf0soFUd94vURgqBq5h7wOTOOi7DlC2S0nj6LA==", 228 | "dependencies": { 229 | "axios": "^1.3.6", 230 | "camelcase-keys": "^7.0.0", 231 | "jsonwebtoken": "^9.0.0", 232 | "long": "^5.0.0", 233 | "protobufjs": "^7.2.4" 234 | } 235 | }, 236 | "node_modules/lodash.includes": { 237 | "version": "4.3.0", 238 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 239 | "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" 240 | }, 241 | "node_modules/lodash.isboolean": { 242 | "version": "3.0.3", 243 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 244 | "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" 245 | }, 246 | "node_modules/lodash.isinteger": { 247 | "version": "4.0.4", 248 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 249 | "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" 250 | }, 251 | "node_modules/lodash.isnumber": { 252 | "version": "3.0.3", 253 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 254 | "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" 255 | }, 256 | "node_modules/lodash.isplainobject": { 257 | "version": "4.0.6", 258 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 259 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" 260 | }, 261 | "node_modules/lodash.isstring": { 262 | "version": "4.0.1", 263 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 264 | "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" 265 | }, 266 | "node_modules/lodash.once": { 267 | "version": "4.1.1", 268 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 269 | "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" 270 | }, 271 | "node_modules/long": { 272 | "version": "5.2.3", 273 | "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", 274 | "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" 275 | }, 276 | "node_modules/lru-cache": { 277 | "version": "6.0.0", 278 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 279 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 280 | "dependencies": { 281 | "yallist": "^4.0.0" 282 | }, 283 | "engines": { 284 | "node": ">=10" 285 | } 286 | }, 287 | "node_modules/map-obj": { 288 | "version": "4.3.0", 289 | "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", 290 | "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", 291 | "engines": { 292 | "node": ">=8" 293 | }, 294 | "funding": { 295 | "url": "https://github.com/sponsors/sindresorhus" 296 | } 297 | }, 298 | "node_modules/mime-db": { 299 | "version": "1.52.0", 300 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 301 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 302 | "engines": { 303 | "node": ">= 0.6" 304 | } 305 | }, 306 | "node_modules/mime-types": { 307 | "version": "2.1.35", 308 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 309 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 310 | "dependencies": { 311 | "mime-db": "1.52.0" 312 | }, 313 | "engines": { 314 | "node": ">= 0.6" 315 | } 316 | }, 317 | "node_modules/ms": { 318 | "version": "2.1.3", 319 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 320 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 321 | }, 322 | "node_modules/node-appwrite": { 323 | "version": "9.0.0", 324 | "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-9.0.0.tgz", 325 | "integrity": "sha512-iTcHbuaJfr6bP/HFkRVV+FcaumKkbINqZyypQdl+tYxv6Dx0bkB/YKUXGYfTkgP18TLPWQQB++OGQhi98dlo2w==", 326 | "dependencies": { 327 | "axios": "^1.3.6", 328 | "form-data": "^4.0.0" 329 | } 330 | }, 331 | "node_modules/prettier": { 332 | "version": "3.0.0", 333 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", 334 | "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", 335 | "dev": true, 336 | "bin": { 337 | "prettier": "bin/prettier.cjs" 338 | }, 339 | "engines": { 340 | "node": ">=14" 341 | }, 342 | "funding": { 343 | "url": "https://github.com/prettier/prettier?sponsor=1" 344 | } 345 | }, 346 | "node_modules/protobufjs": { 347 | "version": "7.2.5", 348 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", 349 | "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", 350 | "hasInstallScript": true, 351 | "dependencies": { 352 | "@protobufjs/aspromise": "^1.1.2", 353 | "@protobufjs/base64": "^1.1.2", 354 | "@protobufjs/codegen": "^2.0.4", 355 | "@protobufjs/eventemitter": "^1.1.0", 356 | "@protobufjs/fetch": "^1.1.0", 357 | "@protobufjs/float": "^1.0.2", 358 | "@protobufjs/inquire": "^1.1.0", 359 | "@protobufjs/path": "^1.1.2", 360 | "@protobufjs/pool": "^1.1.0", 361 | "@protobufjs/utf8": "^1.1.0", 362 | "@types/node": ">=13.7.0", 363 | "long": "^5.0.0" 364 | }, 365 | "engines": { 366 | "node": ">=12.0.0" 367 | } 368 | }, 369 | "node_modules/proxy-from-env": { 370 | "version": "1.1.0", 371 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 372 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 373 | }, 374 | "node_modules/quick-lru": { 375 | "version": "5.1.1", 376 | "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", 377 | "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", 378 | "engines": { 379 | "node": ">=10" 380 | }, 381 | "funding": { 382 | "url": "https://github.com/sponsors/sindresorhus" 383 | } 384 | }, 385 | "node_modules/safe-buffer": { 386 | "version": "5.2.1", 387 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 388 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 389 | "funding": [ 390 | { 391 | "type": "github", 392 | "url": "https://github.com/sponsors/feross" 393 | }, 394 | { 395 | "type": "patreon", 396 | "url": "https://www.patreon.com/feross" 397 | }, 398 | { 399 | "type": "consulting", 400 | "url": "https://feross.org/support" 401 | } 402 | ] 403 | }, 404 | "node_modules/semver": { 405 | "version": "7.5.4", 406 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", 407 | "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", 408 | "dependencies": { 409 | "lru-cache": "^6.0.0" 410 | }, 411 | "bin": { 412 | "semver": "bin/semver.js" 413 | }, 414 | "engines": { 415 | "node": ">=10" 416 | } 417 | }, 418 | "node_modules/type-fest": { 419 | "version": "1.4.0", 420 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", 421 | "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", 422 | "engines": { 423 | "node": ">=10" 424 | }, 425 | "funding": { 426 | "url": "https://github.com/sponsors/sindresorhus" 427 | } 428 | }, 429 | "node_modules/yallist": { 430 | "version": "4.0.0", 431 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 432 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 433 | } 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /functions/livekit-webhook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "livekit-webhook", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/main.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/main.js", 9 | "format": "prettier --write ." 10 | }, 11 | "dependencies": { 12 | "livekit-server-sdk": "^1.2.6", 13 | "node-appwrite": "^9.0.0" 14 | }, 15 | "devDependencies": { 16 | "prettier": "^3.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /functions/livekit-webhook/src/appwrite.js: -------------------------------------------------------------------------------- 1 | import { Client, Databases } from 'node-appwrite'; 2 | 3 | class AppwriteService { 4 | constructor() { 5 | const client = new Client(); 6 | client 7 | .setEndpoint( 8 | process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1' 9 | ) 10 | .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID) 11 | .setKey(process.env.APPWRITE_API_KEY); 12 | 13 | this.databases = new Databases(client); 14 | } 15 | 16 | async doesRoomExist(roomId) { 17 | try { 18 | await this.databases.getDocument( 19 | process.env.MASTER_DATABASE_ID, 20 | process.env.ROOMS_COLLECTION_ID, 21 | roomId 22 | ); 23 | return true; 24 | } catch (err) { 25 | if (err.code !== 404) throw err; 26 | return false; 27 | } 28 | } 29 | 30 | async deleteRoom(roomId) { 31 | // Deleting room doc inside rooms collection in master database 32 | await this.databases.deleteDocument( 33 | process.env.MASTER_DATABASE_ID, 34 | process.env.ROOMS_COLLECTION_ID, 35 | roomId 36 | ); 37 | 38 | // Removing participants from collection 39 | const participantColRef = await this.databases.listDocuments( 40 | process.env.MASTER_DATABASE_ID, 41 | process.env.PARTICIPANTS_COLLECTION_ID, 42 | [Query.equal('roomId', [roomId])] 43 | ); 44 | participantColRef.documents.forEach(async (participant) => { 45 | await this.databases.deleteDocument( 46 | process.env.MASTER_DATABASE_ID, 47 | process.env.PARTICIPANTS_COLLECTION_ID, 48 | participant.$id 49 | ); 50 | }); 51 | } 52 | } 53 | 54 | export default AppwriteService; 55 | -------------------------------------------------------------------------------- /functions/livekit-webhook/src/livekit.js: -------------------------------------------------------------------------------- 1 | import { WebhookReceiver } from 'livekit-server-sdk'; 2 | 3 | class LivekitService { 4 | constructor() { 5 | this.receiver = new WebhookReceiver( 6 | `${process.env.LIVEKIT_API_KEY}`, 7 | `${process.env.LIVEKIT_API_SECRET}` 8 | ); 9 | } 10 | 11 | validateWebhook(context, req) { 12 | try { 13 | const event = this.receiver.receive( 14 | req.bodyRaw, 15 | req.headers['authorization'] 16 | ); 17 | return event; 18 | } catch (err) { 19 | context.error(err); 20 | return null; 21 | } 22 | } 23 | } 24 | 25 | export default LivekitService; 26 | -------------------------------------------------------------------------------- /functions/livekit-webhook/src/main.js: -------------------------------------------------------------------------------- 1 | import AppwriteService from './appwrite.js'; 2 | import LivekitService from './livekit.js'; 3 | import { throwIfMissing } from './utils.js'; 4 | 5 | export default async (context) => { 6 | const { req, res, log, error } = context; 7 | 8 | throwIfMissing(process.env, [ 9 | 'APPWRITE_API_KEY', 10 | 'MASTER_DATABASE_ID', 11 | 'ROOMS_COLLECTION_ID', 12 | 'PARTICIPANTS_COLLECTION_ID', 13 | 'LIVEKIT_API_KEY', 14 | 'LIVEKIT_API_SECRET', 15 | ]); 16 | 17 | const appwrite = new AppwriteService(); 18 | const livekit = new LivekitService(); 19 | 20 | try { 21 | const event = livekit.validateWebhook(context, req); 22 | if (!event) { 23 | return res.json({ success: false }, 401); 24 | } 25 | 26 | log(event); 27 | 28 | if (event.event === 'room_finished') { 29 | // Appwrite room id is same as Livekit room name 30 | const appwriteRoomDocId = event.room.name; 31 | 32 | // Delete the room in appwrite if it still exists 33 | log(appwrite.doesRoomExist(appwriteRoomDocId)); 34 | if (appwrite.doesRoomExist(appwriteRoomDocId)) { 35 | appwrite.deleteRoom(appwriteRoomDocId); 36 | } 37 | } 38 | } catch (e) { 39 | error(String(e)); 40 | return res.json({ success: false }, 500); 41 | } 42 | 43 | return res.json({ success: true }, 200); 44 | }; 45 | -------------------------------------------------------------------------------- /functions/livekit-webhook/src/utils.js: -------------------------------------------------------------------------------- 1 | export const throwIfMissing = (obj, keys) => { 2 | const missing = []; 3 | for (let key of keys) { 4 | if (!(key in obj) || !obj[key]) { 5 | missing.push(key); 6 | } 7 | } 8 | if (missing.length > 0) { 9 | throw new Error(`Missing required fields: ${missing.join(', ')}`); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /functions/match-maker/README.md: -------------------------------------------------------------------------------- 1 | # Match Maker function 2 | 3 | Function to pair users for pair-chat feature. 4 | 5 | ## ⚙️ Configuration 6 | 7 | | Setting | Value | 8 | | ----------------- | ------------------------------ | 9 | | Runtime | Node (18.0) | 10 | | Entrypoint | `src/main.js` | 11 | | Build Commands | `npm install && npm run start` | 12 | | Permissions | `Users` | 13 | | Timeout (Seconds) | 15 | 14 | 15 | ## 🔒 Environment Variables 16 | 17 | ### APPWRITE_API_KEY 18 | 19 | API Key to use Appwrite Sever SDK. 20 | 21 | | Question | Answer | 22 | | ------------- | ------------------------------------------------------------------------ | 23 | | Required | Yes | 24 | | Sample Value | `62...97` | 25 | | Documentation | [Appwrite API Keys](https://appwrite.io/docs/advanced/platform/api-keys) | 26 | 27 | ### DATABASE_ID 28 | 29 | Database ID of master database in appwrite. 30 | 31 | | Question | Answer | 32 | | ------------- | --------------------------------------------------------------------------------------- | 33 | | Required | Yes | 34 | | Sample Value | `Zjc...5PH` | 35 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 36 | 37 | ### REQUESTS_COLLECTION_ID 38 | 39 | Collection ID of requests collection. 40 | 41 | | Question | Answer | 42 | | ------------- | --------------------------------------------------------------------------------------- | 43 | | Required | Yes | 44 | | Sample Value | `NXOi3...IBHDa` | 45 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 46 | 47 | ### ACTIVE_PAIRS_COLLECTION_ID 48 | 49 | Collection ID of active_pairs collection. 50 | 51 | | Question | Answer | 52 | | ------------- | --------------------------------------------------------------------------------------- | 53 | | Required | Yes | 54 | | Sample Value | `NXOi3...IBHDa` | 55 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 56 | -------------------------------------------------------------------------------- /functions/match-maker/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-template", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "starter-template", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "node-appwrite": "^9.0.0" 12 | }, 13 | "devDependencies": { 14 | "prettier": "^3.0.0" 15 | } 16 | }, 17 | "node_modules/asynckit": { 18 | "version": "0.4.0", 19 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 20 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 21 | }, 22 | "node_modules/axios": { 23 | "version": "1.4.0", 24 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", 25 | "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", 26 | "dependencies": { 27 | "follow-redirects": "^1.15.0", 28 | "form-data": "^4.0.0", 29 | "proxy-from-env": "^1.1.0" 30 | } 31 | }, 32 | "node_modules/combined-stream": { 33 | "version": "1.0.8", 34 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 35 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 36 | "dependencies": { 37 | "delayed-stream": "~1.0.0" 38 | }, 39 | "engines": { 40 | "node": ">= 0.8" 41 | } 42 | }, 43 | "node_modules/delayed-stream": { 44 | "version": "1.0.0", 45 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 46 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 47 | "engines": { 48 | "node": ">=0.4.0" 49 | } 50 | }, 51 | "node_modules/follow-redirects": { 52 | "version": "1.15.2", 53 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 54 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 55 | "funding": [ 56 | { 57 | "type": "individual", 58 | "url": "https://github.com/sponsors/RubenVerborgh" 59 | } 60 | ], 61 | "engines": { 62 | "node": ">=4.0" 63 | }, 64 | "peerDependenciesMeta": { 65 | "debug": { 66 | "optional": true 67 | } 68 | } 69 | }, 70 | "node_modules/form-data": { 71 | "version": "4.0.0", 72 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 73 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 74 | "dependencies": { 75 | "asynckit": "^0.4.0", 76 | "combined-stream": "^1.0.8", 77 | "mime-types": "^2.1.12" 78 | }, 79 | "engines": { 80 | "node": ">= 6" 81 | } 82 | }, 83 | "node_modules/mime-db": { 84 | "version": "1.52.0", 85 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 86 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 87 | "engines": { 88 | "node": ">= 0.6" 89 | } 90 | }, 91 | "node_modules/mime-types": { 92 | "version": "2.1.35", 93 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 94 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 95 | "dependencies": { 96 | "mime-db": "1.52.0" 97 | }, 98 | "engines": { 99 | "node": ">= 0.6" 100 | } 101 | }, 102 | "node_modules/node-appwrite": { 103 | "version": "9.0.0", 104 | "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-9.0.0.tgz", 105 | "integrity": "sha512-iTcHbuaJfr6bP/HFkRVV+FcaumKkbINqZyypQdl+tYxv6Dx0bkB/YKUXGYfTkgP18TLPWQQB++OGQhi98dlo2w==", 106 | "dependencies": { 107 | "axios": "^1.3.6", 108 | "form-data": "^4.0.0" 109 | } 110 | }, 111 | "node_modules/prettier": { 112 | "version": "3.0.0", 113 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", 114 | "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", 115 | "dev": true, 116 | "bin": { 117 | "prettier": "bin/prettier.cjs" 118 | }, 119 | "engines": { 120 | "node": ">=14" 121 | }, 122 | "funding": { 123 | "url": "https://github.com/prettier/prettier?sponsor=1" 124 | } 125 | }, 126 | "node_modules/proxy-from-env": { 127 | "version": "1.1.0", 128 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 129 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /functions/match-maker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "match-maker", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/main.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/main.js", 9 | "format": "prettier --write ." 10 | }, 11 | "dependencies": { 12 | "node-appwrite": "^9.0.0" 13 | }, 14 | "devDependencies": { 15 | "prettier": "^3.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /functions/match-maker/src/main.js: -------------------------------------------------------------------------------- 1 | import { Client, Databases, ID, Query } from 'node-appwrite'; 2 | import { throwIfMissing } from './utils.js'; 3 | 4 | export default async ({ req, res, log, error }) => { 5 | throwIfMissing(process.env, [ 6 | 'APPWRITE_API_KEY', 7 | 'DATABASE_ID', 8 | 'REQUESTS_COLLECTION_ID', 9 | 'ACTIVE_PAIRS_COLLECTION_ID', 10 | ]); 11 | 12 | const client = new Client() 13 | .setEndpoint( 14 | process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1' 15 | ) 16 | .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID) 17 | .setKey(process.env.APPWRITE_API_KEY); 18 | 19 | const db = new Databases(client); 20 | 21 | log(req.headers); 22 | const triggerEvent = req.headers['x-appwrite-event']; 23 | const newRequestDocId = triggerEvent.split('.')[5]; 24 | log(newRequestDocId); 25 | 26 | const newRequestDoc = await db.getDocument( 27 | process.env.DATABASE_ID, 28 | process.env.REQUESTS_COLLECTION_ID, 29 | newRequestDocId 30 | ); 31 | 32 | const requestDocsRef = await db.listDocuments( 33 | process.env.DATABASE_ID, 34 | process.env.REQUESTS_COLLECTION_ID, 35 | [ 36 | Query.notEqual('$id', [newRequestDocId]), 37 | Query.equal('languageIso', [newRequestDoc.languageIso]), 38 | Query.equal('isAnonymous', [newRequestDoc.isAnonymous]), 39 | Query.orderAsc('$createdAt'), 40 | Query.limit(25), 41 | ] 42 | ); 43 | log(requestDocsRef.documents); // We get all the requests 44 | 45 | //Check if any other request can be matched 46 | for (let index = 0; index < requestDocsRef.total; index++) { 47 | try { 48 | // Create an active pair document (Gives error if a record with same userDocId exists and then we move to the next request) 49 | const newPairDoc = await db.createDocument( 50 | process.env.DATABASE_ID, 51 | process.env.ACTIVE_PAIRS_COLLECTION_ID, 52 | ID.unique(), 53 | { 54 | uid1: newRequestDoc.uid, 55 | uid2: requestDocsRef.documents[index].uid, 56 | userDocId1: newRequestDocId, 57 | userDocId2: requestDocsRef.documents[index].$id, 58 | ...(newRequestDoc.isAnonymous 59 | ? {} 60 | : { 61 | userName1: newRequestDoc.userName, 62 | userName2: 63 | requestDocsRef.documents[index].userName, 64 | }), 65 | } 66 | ); 67 | log(newPairDoc); 68 | 69 | // Delete requests since we have paired them 70 | await db.deleteDocument( 71 | process.env.DATABASE_ID, 72 | process.env.REQUESTS_COLLECTION_ID, 73 | requestDocsRef.documents[index].$id 74 | ); 75 | await db.deleteDocument( 76 | process.env.DATABASE_ID, 77 | process.env.REQUESTS_COLLECTION_ID, 78 | newRequestDocId 79 | ); 80 | 81 | return res.json({ 82 | message: 'Request was paired', 83 | newPair: newPairDoc, 84 | }); 85 | } catch (e) { 86 | error('That request is already paired: '); 87 | error(String(e)); 88 | } 89 | } 90 | 91 | // If there is no second user or new request was paired with another request, end the execution 92 | return res.json({ 93 | message: 'Request in queue or was paired already.', 94 | }); 95 | }; 96 | -------------------------------------------------------------------------------- /functions/match-maker/src/utils.js: -------------------------------------------------------------------------------- 1 | export const throwIfMissing = (obj, keys) => { 2 | const missing = []; 3 | for (let key of keys) { 4 | if (!(key in obj) || !obj[key]) { 5 | missing.push(key); 6 | } 7 | } 8 | if (missing.length > 0) { 9 | throw new Error(`Missing required fields: ${missing.join(', ')}`); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /functions/send-otp/README.md: -------------------------------------------------------------------------------- 1 | # Send OTP function 2 | 3 | Function to send OTPs. 4 | 5 | ## 🧰 Usage 6 | 7 | ### POST / 8 | 9 | **Parameters** 10 | 11 | | Name | Description | Location | Type | Sample Value | 12 | | -------------- | ------------------------- | -------- | ------ | ------------------ | 13 | | otpId | Document ID of the otp | Body | String | `jcbd...kdsn` | 14 | | recipientEmail | Email ID of the recipient | Body | String | `jcbd...@mail.com` | 15 | 16 | **Response** 17 | 18 | Sample `200` Response: 19 | 20 | ```json 21 | { 22 | "msg": "mail sent" 23 | } 24 | ``` 25 | 26 | ## ⚙️ Configuration 27 | 28 | | Setting | Value | 29 | | ----------------- | ------------------------------ | 30 | | Runtime | Node (18.0) | 31 | | Entrypoint | `src/main.js` | 32 | | Build Commands | `npm install && npm run start` | 33 | | Permissions | `any` | 34 | | Timeout (Seconds) | 15 | 35 | 36 | ## 🔒 Environment Variables 37 | 38 | ### APPWRITE_API_KEY 39 | 40 | API Key to use Appwrite Sever SDK. 41 | 42 | | Question | Answer | 43 | | ------------- | ------------------------------------------------------------------------ | 44 | | Required | Yes | 45 | | Sample Value | `62...97` | 46 | | Documentation | [Appwrite API Keys](https://appwrite.io/docs/advanced/platform/api-keys) | 47 | 48 | ### VERIFICATION_DATABASE_ID 49 | 50 | Database ID of verification database in appwrite. 51 | 52 | | Question | Answer | 53 | | ------------- | --------------------------------------------------------------------------------------- | 54 | | Required | Yes | 55 | | Sample Value | `Zjc...5PH` | 56 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 57 | 58 | ### OTP_COLLECTION_ID 59 | 60 | Collection ID of otp collection. 61 | 62 | | Question | Answer | 63 | | ------------- | --------------------------------------------------------------------------------------- | 64 | | Required | Yes | 65 | | Sample Value | `NXOi3...IBHDa` | 66 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 67 | 68 | ### SENDER_MAIL 69 | 70 | Email of the sender. 71 | 72 | | Question | Answer | 73 | | ------------- | ------------------------------------------------ | 74 | | Required | Yes | 75 | | Sample Value | `jsch...@mail.com` | 76 | | Documentation | [Discord](https://discord.com/invite/6mFZ2S846n) | 77 | 78 | ### SENDER_PASSWORD 79 | 80 | Password of the sender's account. 81 | 82 | | Question | Answer | 83 | | ------------- | ------------------------------------------------ | 84 | | Required | Yes | 85 | | Sample Value | `HC1Itf...........dAAKF5o` | 86 | | Documentation | [Discord](https://discord.com/invite/6mFZ2S846n) | 87 | -------------------------------------------------------------------------------- /functions/send-otp/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-template", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "starter-template", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "node-appwrite": "^9.0.0", 12 | "nodemailer": "^6.9.5" 13 | }, 14 | "devDependencies": { 15 | "prettier": "^3.0.0" 16 | } 17 | }, 18 | "node_modules/asynckit": { 19 | "version": "0.4.0", 20 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 21 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 22 | }, 23 | "node_modules/axios": { 24 | "version": "1.4.0", 25 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", 26 | "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", 27 | "dependencies": { 28 | "follow-redirects": "^1.15.0", 29 | "form-data": "^4.0.0", 30 | "proxy-from-env": "^1.1.0" 31 | } 32 | }, 33 | "node_modules/combined-stream": { 34 | "version": "1.0.8", 35 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 36 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 37 | "dependencies": { 38 | "delayed-stream": "~1.0.0" 39 | }, 40 | "engines": { 41 | "node": ">= 0.8" 42 | } 43 | }, 44 | "node_modules/delayed-stream": { 45 | "version": "1.0.0", 46 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 47 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 48 | "engines": { 49 | "node": ">=0.4.0" 50 | } 51 | }, 52 | "node_modules/follow-redirects": { 53 | "version": "1.15.2", 54 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 55 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 56 | "funding": [ 57 | { 58 | "type": "individual", 59 | "url": "https://github.com/sponsors/RubenVerborgh" 60 | } 61 | ], 62 | "engines": { 63 | "node": ">=4.0" 64 | }, 65 | "peerDependenciesMeta": { 66 | "debug": { 67 | "optional": true 68 | } 69 | } 70 | }, 71 | "node_modules/form-data": { 72 | "version": "4.0.0", 73 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 74 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 75 | "dependencies": { 76 | "asynckit": "^0.4.0", 77 | "combined-stream": "^1.0.8", 78 | "mime-types": "^2.1.12" 79 | }, 80 | "engines": { 81 | "node": ">= 6" 82 | } 83 | }, 84 | "node_modules/mime-db": { 85 | "version": "1.52.0", 86 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 87 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 88 | "engines": { 89 | "node": ">= 0.6" 90 | } 91 | }, 92 | "node_modules/mime-types": { 93 | "version": "2.1.35", 94 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 95 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 96 | "dependencies": { 97 | "mime-db": "1.52.0" 98 | }, 99 | "engines": { 100 | "node": ">= 0.6" 101 | } 102 | }, 103 | "node_modules/node-appwrite": { 104 | "version": "9.0.0", 105 | "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-9.0.0.tgz", 106 | "integrity": "sha512-iTcHbuaJfr6bP/HFkRVV+FcaumKkbINqZyypQdl+tYxv6Dx0bkB/YKUXGYfTkgP18TLPWQQB++OGQhi98dlo2w==", 107 | "dependencies": { 108 | "axios": "^1.3.6", 109 | "form-data": "^4.0.0" 110 | } 111 | }, 112 | "node_modules/nodemailer": { 113 | "version": "6.9.5", 114 | "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.5.tgz", 115 | "integrity": "sha512-/dmdWo62XjumuLc5+AYQZeiRj+PRR8y8qKtFCOyuOl1k/hckZd8durUUHs/ucKx6/8kN+wFxqKJlQ/LK/qR5FA==", 116 | "engines": { 117 | "node": ">=6.0.0" 118 | } 119 | }, 120 | "node_modules/prettier": { 121 | "version": "3.0.0", 122 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", 123 | "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", 124 | "dev": true, 125 | "bin": { 126 | "prettier": "bin/prettier.cjs" 127 | }, 128 | "engines": { 129 | "node": ">=14" 130 | }, 131 | "funding": { 132 | "url": "https://github.com/prettier/prettier?sponsor=1" 133 | } 134 | }, 135 | "node_modules/proxy-from-env": { 136 | "version": "1.1.0", 137 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 138 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /functions/send-otp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "send-otp", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/main.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/main.js", 9 | "format": "prettier --write ." 10 | }, 11 | "dependencies": { 12 | "node-appwrite": "^9.0.0", 13 | "nodemailer": "^6.9.5" 14 | }, 15 | "devDependencies": { 16 | "prettier": "^3.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /functions/send-otp/src/appwrite.js: -------------------------------------------------------------------------------- 1 | import { Client, Databases } from 'node-appwrite'; 2 | 3 | class AppwriteService { 4 | constructor() { 5 | const client = new Client() 6 | .setEndpoint( 7 | process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1' 8 | ) 9 | .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID) 10 | .setKey(process.env.APPWRITE_API_KEY); 11 | 12 | this.databases = new Databases(client); 13 | } 14 | 15 | async createOtpDocument(otpId, otp, date) { 16 | await this.databases.createDocument( 17 | process.env.VERIFICATION_DATABASE_ID, 18 | process.env.OTP_COLLECTION_ID, 19 | otpId, 20 | { 21 | otp, 22 | date 23 | } 24 | ); 25 | } 26 | } 27 | 28 | export default AppwriteService; 29 | -------------------------------------------------------------------------------- /functions/send-otp/src/mail.js: -------------------------------------------------------------------------------- 1 | import { createTransport } from 'nodemailer'; 2 | 3 | class MailService { 4 | constructor() { 5 | this.transporter = createTransport({ 6 | service: 'gmail', 7 | secure: true, 8 | auth: { 9 | user: process.env.SENDER_MAIL, 10 | pass: process.env.SENDER_PASSWORD, 11 | }, 12 | }); 13 | } 14 | 15 | async sendMail(recipientEmail, otp) { 16 | await this.transporter.sendMail({ 17 | from: process.env.SENDER_MAIL, // sender address 18 | to: recipientEmail, // list of receivers 19 | subject: 'Email Verification', // Subject line 20 | text: `Greetings User, here is your email verification OTP: ${otp}`, // plain text body 21 | }); 22 | } 23 | } 24 | 25 | export default MailService; 26 | -------------------------------------------------------------------------------- /functions/send-otp/src/main.js: -------------------------------------------------------------------------------- 1 | import { throwIfMissing } from "./utils.js"; 2 | import AppwriteService from "./appwrite.js"; 3 | import MailService from "./mail.js"; 4 | 5 | export default async ({ req, res, log, error }) => { 6 | throwIfMissing(process.env, [ 7 | "APPWRITE_API_KEY", 8 | "VERIFICATION_DATABASE_ID", 9 | "OTP_COLLECTION_ID", 10 | "SENDER_MAIL", 11 | "SENDER_PASSWORD", 12 | ]); 13 | 14 | const appwrite = new AppwriteService(); 15 | const mailService = new MailService(); 16 | 17 | try { 18 | log(req.body); 19 | const { otpID, email: recipientEmail } = JSON.parse(req.body); 20 | 21 | const otp = String(Math.floor(100000 + Math.random() * 900000)); 22 | 23 | await mailService.sendMail(recipientEmail, otp); 24 | 25 | // Logic for deleting the otp when the Date changes 26 | const currentDate = new Date().toDateString(); 27 | log(`Current Date: ${currentDate}`); 28 | 29 | await appwrite.createOtpDocument(otpID, otp, currentDate); 30 | } catch (e) { 31 | error(String(e)); 32 | return res.json({ message: String(e) }); 33 | } 34 | 35 | return res.json({ message: "mail sent" }); 36 | }; 37 | -------------------------------------------------------------------------------- /functions/send-otp/src/utils.js: -------------------------------------------------------------------------------- 1 | export const throwIfMissing = (obj, keys) => { 2 | const missing = []; 3 | for (let key of keys) { 4 | if (!(key in obj) || !obj[key]) { 5 | missing.push(key); 6 | } 7 | } 8 | if (missing.length > 0) { 9 | throw new Error(`Missing required fields: ${missing.join(', ')}`); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /functions/upcomingRoom-isTime-checker/README.md: -------------------------------------------------------------------------------- 1 | # Upcoming Room Time Checking function 2 | A Cron Function to check all the exsistent upcoming room scheduled timings and comparing to current time in order to activate a upcoming room if the current time is less than 5 minutes away from the scheduled time 3 | 4 | ## 🧰 Usage 5 | It is a Cron function repeating itself after every 5 minutes 6 | 7 | ### How does it work 8 | Fetches all the list of upcoming rooms, for each upcoming room compares the time difference from current time to scheduled, if the difference is less than or equal to 5 minutes updates the isTime attrubute of that upcoming room document in the upcoming room collection to "True" and sends a push notification to all the subscribers of that upcoming room via FCM -------------------------------------------------------------------------------- /functions/upcomingRoom-isTime-checker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "appwrite-function", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "firebase-admin": "^11.11.0", 14 | "node-appwrite": "^8.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /functions/upcomingRoom-isTime-checker/src/main.js: -------------------------------------------------------------------------------- 1 | const sdk = require("node-appwrite"); 2 | 3 | const admin = require('firebase-admin'); 4 | // const { getMessaging } = require('firebase-admin/messaging'); 5 | // const serviceAccount = require("./resonate-service-account.json"); 6 | // const app = admin.initializeApp({ 7 | // credential: admin.credential.cert(serviceAccount) 8 | // }); 9 | 10 | module.exports = async function ({ req, res, log }) { 11 | var subscribersTokens = []; 12 | const client = new sdk.Client(); 13 | const database = new sdk.Databases(client); 14 | const query = sdk.Query; 15 | log("here"); 16 | client.setEndpoint( 17 | process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1' 18 | ) 19 | .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID) 20 | .setKey(process.env.APPWRITE_API_KEY); 21 | log(process.env.APPWRITE_FUNCTION_PROJECT_ID); 22 | log("here also"); 23 | let upcomingRoomsList = await database.listDocuments(process.env.UpcomingRoomsDataBaseID, process.env.UpcomingRoomsCollectionID); 24 | log("here as well"); 25 | for (const document of upcomingRoomsList.documents) { 26 | log("now here"); 27 | var scheduledDateTime = document["scheduledDateTime"]; 28 | log(scheduledDateTime); 29 | var splittedDateTime = scheduledDateTime.split('T'); 30 | var extractedDate = splittedDateTime[0]; 31 | var extractedTime = splittedDateTime[1]; 32 | var SplittingDate = extractedDate.split("-"); 33 | const year = Number(SplittingDate[0]); 34 | const month = Number(SplittingDate[1]); 35 | const day = Number(SplittingDate[2]); 36 | var SplittingTime = extractedTime.split(":"); 37 | const hour = Number(SplittingTime[0]); 38 | const minutes = Number(SplittingTime[1]); 39 | const upcomingRoomDate = Date.UTC(year, month - 1, day, hour, minutes); 40 | log(upcomingRoomDate); 41 | const now = new Date(); 42 | const nowTime = now.getTime(); 43 | log(nowTime); 44 | 45 | var timeLeft = upcomingRoomDate - nowTime; 46 | var timeLeftInMinutes = timeLeft / (1000 * 60); 47 | log(timeLeftInMinutes); 48 | if (timeLeftInMinutes <= 5 && timeLeftInMinutes >= -5 && document["isTime"] == false) { 49 | await database.updateDocument(process.env.UpcomingRoomsDataBaseID, process.env.UpcomingRoomsCollectionID, document.$id, { 50 | "isTime": true 51 | }) 52 | // log("Send Notification"); 53 | // let subscriberList = await database.listDocuments(process.env.UpcomingRoomsDataBaseID, process.env.SubscriberCollectionID, [query.equal('upcomingRoomId', [document.$id])]); 54 | // log("here as well") 55 | // subscriberList.documents.forEach(subscriber => { 56 | // for (const token of subscriber["registrationTokens"]) { 57 | // subscribersTokens.push(token); 58 | // } 59 | // }); 60 | // for (const creator_token of document["creator_fcm_tokens"]) { 61 | // subscribersTokens.push(creator_token); 62 | // } 63 | // log(subscribersTokens); 64 | // const message = { 65 | // notification: { 66 | // title: 'Room Reminder', 67 | // body: `The room ${document["name"]} will Start Soon` 68 | // }, 69 | // tokens: subscribersTokens, 70 | // priority: "high", 71 | // android: { 72 | // priority: "high" 73 | // } 74 | // }; 75 | // getMessaging(app).sendEachForMulticast(message) 76 | // .then((response) => { 77 | // if (response.failureCount > 0) { 78 | // log('Failed'); 79 | // } else { 80 | // log('Notifications were sent successfully'); 81 | // } 82 | // }); 83 | } 84 | } 85 | return res.json({ 86 | message: 'set verified' 87 | }); 88 | }; 89 | -------------------------------------------------------------------------------- /functions/verify-email/README.md: -------------------------------------------------------------------------------- 1 | # Verify Email function 2 | 3 | Function to verify email ID of users. 4 | 5 | ## ⚙️ Configuration 6 | 7 | | Setting | Value | 8 | | ----------------- | ------------------------------ | 9 | | Runtime | Node (18.0) | 10 | | Entrypoint | `src/main.js` | 11 | | Build Commands | `npm install && npm run start` | 12 | | Permissions | `Users` | 13 | | Timeout (Seconds) | 15 | 14 | 15 | ## 🔒 Environment Variables 16 | 17 | ### APPWRITE_API_KEY 18 | 19 | API Key to use Appwrite Sever SDK. 20 | 21 | | Question | Answer | 22 | | ------------- | ------------------------------------------------------------------------ | 23 | | Required | Yes | 24 | | Sample Value | `62...97` | 25 | | Documentation | [Appwrite API Keys](https://appwrite.io/docs/advanced/platform/api-keys) | 26 | -------------------------------------------------------------------------------- /functions/verify-email/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verify-email", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "verify-email", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "node-appwrite": "^9.0.0" 12 | }, 13 | "devDependencies": { 14 | "prettier": "^3.0.0" 15 | } 16 | }, 17 | "node_modules/asynckit": { 18 | "version": "0.4.0", 19 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 20 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 21 | }, 22 | "node_modules/axios": { 23 | "version": "1.4.0", 24 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", 25 | "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", 26 | "dependencies": { 27 | "follow-redirects": "^1.15.0", 28 | "form-data": "^4.0.0", 29 | "proxy-from-env": "^1.1.0" 30 | } 31 | }, 32 | "node_modules/combined-stream": { 33 | "version": "1.0.8", 34 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 35 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 36 | "dependencies": { 37 | "delayed-stream": "~1.0.0" 38 | }, 39 | "engines": { 40 | "node": ">= 0.8" 41 | } 42 | }, 43 | "node_modules/delayed-stream": { 44 | "version": "1.0.0", 45 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 46 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 47 | "engines": { 48 | "node": ">=0.4.0" 49 | } 50 | }, 51 | "node_modules/follow-redirects": { 52 | "version": "1.15.2", 53 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 54 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 55 | "funding": [ 56 | { 57 | "type": "individual", 58 | "url": "https://github.com/sponsors/RubenVerborgh" 59 | } 60 | ], 61 | "engines": { 62 | "node": ">=4.0" 63 | }, 64 | "peerDependenciesMeta": { 65 | "debug": { 66 | "optional": true 67 | } 68 | } 69 | }, 70 | "node_modules/form-data": { 71 | "version": "4.0.0", 72 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 73 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 74 | "dependencies": { 75 | "asynckit": "^0.4.0", 76 | "combined-stream": "^1.0.8", 77 | "mime-types": "^2.1.12" 78 | }, 79 | "engines": { 80 | "node": ">= 6" 81 | } 82 | }, 83 | "node_modules/mime-db": { 84 | "version": "1.52.0", 85 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 86 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 87 | "engines": { 88 | "node": ">= 0.6" 89 | } 90 | }, 91 | "node_modules/mime-types": { 92 | "version": "2.1.35", 93 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 94 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 95 | "dependencies": { 96 | "mime-db": "1.52.0" 97 | }, 98 | "engines": { 99 | "node": ">= 0.6" 100 | } 101 | }, 102 | "node_modules/node-appwrite": { 103 | "version": "9.0.0", 104 | "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-9.0.0.tgz", 105 | "integrity": "sha512-iTcHbuaJfr6bP/HFkRVV+FcaumKkbINqZyypQdl+tYxv6Dx0bkB/YKUXGYfTkgP18TLPWQQB++OGQhi98dlo2w==", 106 | "dependencies": { 107 | "axios": "^1.3.6", 108 | "form-data": "^4.0.0" 109 | } 110 | }, 111 | "node_modules/prettier": { 112 | "version": "3.0.0", 113 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", 114 | "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", 115 | "dev": true, 116 | "bin": { 117 | "prettier": "bin/prettier.cjs" 118 | }, 119 | "engines": { 120 | "node": ">=14" 121 | }, 122 | "funding": { 123 | "url": "https://github.com/prettier/prettier?sponsor=1" 124 | } 125 | }, 126 | "node_modules/proxy-from-env": { 127 | "version": "1.1.0", 128 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 129 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /functions/verify-email/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verify-email", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/main.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/main.js", 9 | "format": "prettier --write ." 10 | }, 11 | "dependencies": { 12 | "node-appwrite": "^9.0.0" 13 | }, 14 | "devDependencies": { 15 | "prettier": "^3.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /functions/verify-email/src/main.js: -------------------------------------------------------------------------------- 1 | import { Client, Users } from 'node-appwrite'; 2 | import { throwIfMissing } from './utils.js'; 3 | 4 | export default async ({ req, res, log, error }) => { 5 | throwIfMissing(process.env, ['APPWRITE_API_KEY']); 6 | 7 | const client = new Client() 8 | .setEndpoint( 9 | process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1' 10 | ) 11 | .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID) 12 | .setKey(process.env.APPWRITE_API_KEY); 13 | 14 | try { 15 | log(req.body); 16 | const { userID } = JSON.parse(req.body); 17 | 18 | // Update email verification status of the user 19 | await new Users(client).updateEmailVerification(userID, true); 20 | } catch (e) { 21 | error(String(e)); 22 | return res.json({ message: String(e) }, 500); 23 | } 24 | 25 | return res.json({ message: 'null' }); 26 | }; 27 | -------------------------------------------------------------------------------- /functions/verify-email/src/utils.js: -------------------------------------------------------------------------------- 1 | export const throwIfMissing = (obj, keys) => { 2 | const missing = []; 3 | for (let key of keys) { 4 | if (!(key in obj) || !obj[key]) { 5 | missing.push(key); 6 | } 7 | } 8 | if (missing.length > 0) { 9 | throw new Error(`Missing required fields: ${missing.join(', ')}`); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /functions/verify-otp/README.md: -------------------------------------------------------------------------------- 1 | # Verify OTP function 2 | 3 | Function to verify OTPs. 4 | 5 | ## 🧰 Usage 6 | 7 | ### POST / 8 | 9 | **Parameters** 10 | 11 | | Name | Description | Location | Type | Sample Value | 12 | | -------------- | ------------------------- | -------- | ------ | ------------------ | 13 | | otpId | Document ID of the otp | Body | String | `jcbd...kdsn` | 14 | | userOTP | otp sent by user | Body | String | `123456` | 15 | | verify_ID | Document ID of the otp | Body | String | `jcbd...kdsn` | 16 | 17 | ## ⚙️ Configuration 18 | 19 | | Setting | Value | 20 | | ----------------- | ------------------------------ | 21 | | Runtime | Node (18.0) | 22 | | Entrypoint | `src/main.js` | 23 | | Build Commands | `npm install && npm run start` | 24 | | Permissions | `Users` | 25 | | Timeout (Seconds) | 15 | 26 | 27 | ## 🔒 Environment Variables 28 | 29 | ### APPWRITE_API_KEY 30 | 31 | API Key to use Appwrite Sever SDK. 32 | 33 | | Question | Answer | 34 | | ------------- | ------------------------------------------------------------------------ | 35 | | Required | Yes | 36 | | Sample Value | `62...97` | 37 | | Documentation | [Appwrite API Keys](https://appwrite.io/docs/advanced/platform/api-keys) | 38 | 39 | ### VERIFICATION_DATABASE_ID 40 | 41 | Database ID of verification database in appwrite. 42 | 43 | | Question | Answer | 44 | | ------------- | --------------------------------------------------------------------------------------- | 45 | | Required | Yes | 46 | | Sample Value | `Zjc...5PH` | 47 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 48 | 49 | ### OTP_COLLECTION_ID 50 | 51 | Collection ID of otp collection. 52 | 53 | | Question | Answer | 54 | | ------------- | --------------------------------------------------------------------------------------- | 55 | | Required | Yes | 56 | | Sample Value | `NXOi3...IBHDa` | 57 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | 58 | 59 | ### VERIFY_COLLECTION_ID 60 | 61 | Collection ID of verify collection. 62 | 63 | | Question | Answer | 64 | | ------------- | --------------------------------------------------------------------------------------- | 65 | | Required | Yes | 66 | | Sample Value | `NXOi3...IBHDa` | 67 | | Documentation | [Resonate](https://github.com/AOSSIE-Org/Resonate/blob/master/lib/utils/constants.dart) | -------------------------------------------------------------------------------- /functions/verify-otp/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verify-otp", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "verify-otp", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "node-appwrite": "^9.0.0" 12 | }, 13 | "devDependencies": { 14 | "prettier": "^3.0.0" 15 | } 16 | }, 17 | "node_modules/asynckit": { 18 | "version": "0.4.0", 19 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 20 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 21 | }, 22 | "node_modules/axios": { 23 | "version": "1.4.0", 24 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", 25 | "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", 26 | "dependencies": { 27 | "follow-redirects": "^1.15.0", 28 | "form-data": "^4.0.0", 29 | "proxy-from-env": "^1.1.0" 30 | } 31 | }, 32 | "node_modules/combined-stream": { 33 | "version": "1.0.8", 34 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 35 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 36 | "dependencies": { 37 | "delayed-stream": "~1.0.0" 38 | }, 39 | "engines": { 40 | "node": ">= 0.8" 41 | } 42 | }, 43 | "node_modules/delayed-stream": { 44 | "version": "1.0.0", 45 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 46 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 47 | "engines": { 48 | "node": ">=0.4.0" 49 | } 50 | }, 51 | "node_modules/follow-redirects": { 52 | "version": "1.15.2", 53 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 54 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 55 | "funding": [ 56 | { 57 | "type": "individual", 58 | "url": "https://github.com/sponsors/RubenVerborgh" 59 | } 60 | ], 61 | "engines": { 62 | "node": ">=4.0" 63 | }, 64 | "peerDependenciesMeta": { 65 | "debug": { 66 | "optional": true 67 | } 68 | } 69 | }, 70 | "node_modules/form-data": { 71 | "version": "4.0.0", 72 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 73 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 74 | "dependencies": { 75 | "asynckit": "^0.4.0", 76 | "combined-stream": "^1.0.8", 77 | "mime-types": "^2.1.12" 78 | }, 79 | "engines": { 80 | "node": ">= 6" 81 | } 82 | }, 83 | "node_modules/mime-db": { 84 | "version": "1.52.0", 85 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 86 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 87 | "engines": { 88 | "node": ">= 0.6" 89 | } 90 | }, 91 | "node_modules/mime-types": { 92 | "version": "2.1.35", 93 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 94 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 95 | "dependencies": { 96 | "mime-db": "1.52.0" 97 | }, 98 | "engines": { 99 | "node": ">= 0.6" 100 | } 101 | }, 102 | "node_modules/node-appwrite": { 103 | "version": "9.0.0", 104 | "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-9.0.0.tgz", 105 | "integrity": "sha512-iTcHbuaJfr6bP/HFkRVV+FcaumKkbINqZyypQdl+tYxv6Dx0bkB/YKUXGYfTkgP18TLPWQQB++OGQhi98dlo2w==", 106 | "dependencies": { 107 | "axios": "^1.3.6", 108 | "form-data": "^4.0.0" 109 | } 110 | }, 111 | "node_modules/prettier": { 112 | "version": "3.0.0", 113 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", 114 | "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", 115 | "dev": true, 116 | "bin": { 117 | "prettier": "bin/prettier.cjs" 118 | }, 119 | "engines": { 120 | "node": ">=14" 121 | }, 122 | "funding": { 123 | "url": "https://github.com/prettier/prettier?sponsor=1" 124 | } 125 | }, 126 | "node_modules/proxy-from-env": { 127 | "version": "1.1.0", 128 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 129 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /functions/verify-otp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verify-otp", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/main.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/main.js", 9 | "format": "prettier --write ." 10 | }, 11 | "dependencies": { 12 | "node-appwrite": "^9.0.0" 13 | }, 14 | "devDependencies": { 15 | "prettier": "^3.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /functions/verify-otp/src/main.js: -------------------------------------------------------------------------------- 1 | import { Client, Databases } from 'node-appwrite'; 2 | import { throwIfMissing } from './utils.js'; 3 | 4 | export default async ({ req, res, log, error }) => { 5 | throwIfMissing(process.env, [ 6 | 'APPWRITE_API_KEY', 7 | 'VERIFICATION_DATABASE_ID', 8 | 'OTP_COLLECTION_ID', 9 | 'VERIFY_COLLECTION_ID', 10 | ]); 11 | 12 | const client = new Client() 13 | .setEndpoint( 14 | process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1' 15 | ) 16 | .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID) 17 | .setKey(process.env.APPWRITE_API_KEY); 18 | 19 | const db = new Databases(client); 20 | log(req.body); 21 | const { 22 | otpID, 23 | userOTP: userOtp, 24 | verify_ID: verificationId, 25 | } = JSON.parse(req.body); 26 | 27 | let otpDocument; 28 | 29 | try { 30 | otpDocument = await db.getDocument( 31 | process.env.VERIFICATION_DATABASE_ID, 32 | process.env.OTP_COLLECTION_ID, 33 | otpID 34 | ); 35 | } catch (e) { 36 | log("error in getting the otp doc") 37 | error(String(e)); 38 | return res.json({ message: String(e) }, 500); 39 | } 40 | 41 | try { 42 | await db.createDocument( 43 | process.env.VERIFICATION_DATABASE_ID, 44 | process.env.VERIFY_COLLECTION_ID, 45 | verificationId, 46 | { 47 | status: String(otpDocument.otp === userOtp), 48 | } 49 | ); 50 | } catch (e) { 51 | log("error in creating the verification doc") 52 | error(String(e)); 53 | return res.json({ message: String(e) }, 500); 54 | } 55 | return res.json({ message: 'null' }, 200); 56 | }; 57 | -------------------------------------------------------------------------------- /functions/verify-otp/src/utils.js: -------------------------------------------------------------------------------- 1 | export const throwIfMissing = (obj, keys) => { 2 | const missing = []; 3 | for (let key of keys) { 4 | if (!(key in obj) || !obj[key]) { 5 | missing.push(key); 6 | } 7 | } 8 | if (missing.length > 0) { 9 | throw new Error(`Missing required fields: ${missing.join(', ')}`); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /init-auth.ps1: -------------------------------------------------------------------------------- 1 | # Rn this file contains chunks of commands that could help get the auth initialization set up quickly 2 | 3 | 4 | # Write-Host "Installing devtunnels" 5 | 6 | # Invoke-WebRequest -Uri https://aka.ms/TunnelsCliDownload/win-x64 -OutFile devtunnel.exe 7 | # .\devtunnel.exe -h 8 | 9 | # while ($true) { 10 | # devtunnel login 11 | # if ($LASTEXITCODE -eq 0) { 12 | # break 13 | # } else { 14 | # Write-Host "devtunnel login failed. Please try again." 15 | # } 16 | # } 17 | 18 | # function Check-Status { 19 | # param ($processName) 20 | 21 | # if ($LASTEXITCODE -ne 0) { 22 | # Write-Host "Error: $processName failed to start. Exiting script." 23 | # exit 1 24 | # } 25 | # } 26 | 27 | # # Start the first devtunnel on port 80 28 | # Write-Host "Starting devtunnel on port 80..." 29 | # Start-Process "devtunnel" -ArgumentList "host -p 80 --allow-anonymous --protocol http --host-header unchanged" -NoNewWindow -RedirectStandardOutput $null -RedirectStandardError $null 30 | # Start-Sleep -Seconds 2 31 | # Check-Status "devtunnel on port 80" 32 | 33 | # # Start the second devtunnel on port 7880 34 | # Write-Host "Starting devtunnel on port 7880..." 35 | # Start-Process "devtunnel" -ArgumentList "host -p 7880 --allow-anonymous --protocol http --host-header unchanged" -NoNewWindow -RedirectStandardOutput $null -RedirectStandardError $null 36 | # Start-Sleep -Seconds 2 37 | # Check-Status "devtunnel on port 7880" 38 | 39 | # Write-Host "Both devtunnels started successfully." 40 | 41 | # # Get the list of active tunnels and extract the URLs 42 | # Write-Host "Fetching the list of active tunnels" 43 | # $tunnel_list = devtunnel list 44 | 45 | # Write-Host $tunnel_list 46 | 47 | # # Extract tunnel IDs from the list 48 | # $tunnel_ids = $tunnel_list -match '\b[a-z0-9-]*\.inc1\b' | ForEach-Object { $_.Matches.Value } 49 | 50 | # # Split the IDs into individual variables if needed 51 | # $tunnel_id_1 = $tunnel_ids[0] 52 | # $tunnel_id_2 = $tunnel_ids[1] 53 | 54 | # # Output the extracted tunnel IDs 55 | # Write-Host "Tunnel ID for Appwrite: $tunnel_id_1" 56 | # Write-Host "Tunnel ID for Livekit: $tunnel_id_2" 57 | 58 | 59 | # Ask contributor for Oauth2 provider config (Google. Github) 60 | # Write-Host "Please follow the Set Up Guide on Resonate to create the Oauth2 credentials for Google and Github" 61 | 62 | # Write-Host "ngrok tunnel Domain Name: $ngrok_url" 63 | # $googleAppId = Read-Host "Enter the Google App ID" 64 | # $googleSecret = Read-Host "Enter the Google App Secret" 65 | # appwrite projects update-o-auth-2 --project-id $projectId --provider 'google' --appId $googleAppId --secret $googleSecret --enabled $true 66 | 67 | # $githubAppId = Read-Host "Enter the GitHub App ID" 68 | # $githubSecret = Read-Host "Enter the GitHub App Secret" 69 | # appwrite projects update-o-auth-2 --project-id $projectId --provider 'github' --appId $githubAppId --secret $githubSecret --enabled $true 70 | -------------------------------------------------------------------------------- /init-auth.sh: -------------------------------------------------------------------------------- 1 | # Rn this file contains chunks of commands that could help get the auth initialization set up quickly 2 | 3 | ## Install devtunnel command for macOS 4 | # brew install --cask devtunnel 5 | 6 | # Install devtunnel command for linux 7 | # curl -sL https://aka.ms/DevTunnelCliInstall | bash 8 | 9 | ## Devtunnel login command 10 | # while true; do 11 | # devtunnel login 12 | # if [ $? -eq 0 ]; then 13 | # break 14 | # else 15 | # echo "devtunnel login failed. Please try again." 16 | # fi 17 | # done 18 | 19 | 20 | 21 | ## Start Devtunnels and get the Url's 22 | # check_status() { 23 | # if [ $? -ne 0 ]; then 24 | # echo "Error: $1 failed to start. Exiting script." 25 | # exit 1 26 | # fi 27 | # } 28 | # Start the first devtunnel on port 80 29 | # echo "Starting devtunnel on port 80..." 30 | # devtunnel host -p 80 --allow-anonymous --protocol http --host-header unchanged > /dev/null 2>&1 & 31 | # check_status "devtunnel on port 80" 32 | 33 | # # Start the second devtunnel on port 7880 34 | # echo "Starting devtunnel on port 7880..." 35 | # devtunnel host -p 7880 --allow-anonymous --protocol http --host-header unchanged > /dev/null 2>&1 & 36 | # check_status "devtunnel on port 7880" 37 | 38 | # sleep 2 39 | 40 | # echo "Both devtunnels started successfully." 41 | 42 | # Get the list of active tunnels and extract the URLs 43 | # echo "Fetching the list of active tunnels" 44 | # tunnel_list=$(devtunnel list) 45 | 46 | # echo $tunnel_list 47 | # tunnel_ids=$(echo "$tunnel_list" | grep -o '\b[a-z0-9-]*\.inc1\b') 48 | 49 | # # Split the IDs into individual variables if needed 50 | # tunnel_id_1=$(echo "$tunnel_ids" | sed -n '1p') 51 | # tunnel_id_2=$(echo "$tunnel_ids" | sed -n '2p') 52 | 53 | # # Output the extracted tunnel IDs 54 | # echo "Tunnel ID for Appwrite: $tunnel_id_1" 55 | # echo "Tunnel ID for Livekit: $tunnel_id_2" 56 | 57 | 58 | ## Start initializing Auth 59 | 60 | # Ask contributor for Oauth2 provider config (Google. Github) 61 | # echo "Please follow the Set Up Guide on Resonate to create the Oauth2 credentials for Google and Github" 62 | 63 | # read -p "Enter the Google App ID: " googleAppId 64 | # read -p "Enter the Google App Secret: " googleSecret 65 | # appwrite projects update-o-auth-2 --project-id "$projectId" --provider 'google' --appId "$googleAppId" --secret "$googleSecret" --enabled true 66 | 67 | # read -p "Enter the GitHub App ID: " githubAppId 68 | # read -p "Enter the GitHub App Secret: " githubSecret 69 | # appwrite projects update-o-auth-2 --project-id "$projectId" --provider 'github' --appId "$githubAppId" --secret "$githubSecret" --enabled true -------------------------------------------------------------------------------- /init.ps1: -------------------------------------------------------------------------------- 1 | Write-Host "Installing Dependencies...." 2 | 3 | 4 | # Set the execution policy to RemoteSigned for the current user 5 | Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force 6 | 7 | # Function to check if Scoop is installed 8 | function Is-ScoopInstalled { 9 | $scoopPath = "$env:USERPROFILE\scoop\shims\scoop.ps1" 10 | return Test-Path $scoopPath 11 | } 12 | 13 | # Check if Scoop is installed 14 | if (Is-ScoopInstalled) { 15 | Write-Host "Scoop is already installed." 16 | } else { 17 | Write-Host "Scoop is not installed. Installing Scoop..." 18 | Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression 19 | } 20 | 21 | # Verify installation 22 | if (Is-ScoopInstalled) { 23 | Write-Host "Scoop has been successfully installed." 24 | } else { 25 | Write-Host "Failed to install Scoop." 26 | } 27 | 28 | 29 | Write-Host "Install Appwrite-cli using Scoop" 30 | scoop install https://raw.githubusercontent.com/appwrite/sdk-for-cli/master/scoop/appwrite.json 31 | 32 | docker run -it --add-host host.docker.internal:host-gateway --rm ` 33 | --volume /var/run/docker.sock:/var/run/docker.sock ` 34 | --volume "$(pwd)/appwrite:/usr/src/code/appwrite:rw" ` 35 | --entrypoint="install" ` 36 | appwrite/appwrite:1.6.1 37 | 38 | $projectId = "resonate" 39 | 40 | # Remove previous Appwrite Cli data 41 | Remove-Item -Recurse -Force $HOME\.appwrite 42 | 43 | # Ask contributor account credentials 44 | while ($true) { 45 | appwrite login --endpoint "http://localhost:80/v1" 46 | if ($LASTEXITCODE -eq 0) { 47 | break 48 | } else { 49 | Write-Host "Login failed. Please try again." 50 | } 51 | } 52 | 53 | Write-Host "Starting resonate project set up...." 54 | # Get team id for project creation 55 | $teamId = Read-Host "Please provide the team Id as instructed in the Resonate Set Up Guide" 56 | 57 | # Creating the project 58 | appwrite projects create --project-id resonate --name Resonate --team-id $teamId 59 | 60 | # Creating IOS and Android platforms 61 | appwrite projects create-platform --project-id $projectId --type flutter-android --key com.resonate.resonate --name Resonate 62 | appwrite projects create-platform --project-id $projectId --type flutter-ios --key com.resonate.resonate --name Resonate 63 | 64 | # Creating Server Key and Retrieving it from response 65 | $create_key_response = appwrite projects create-key --project-id $projectId --name "Appwrite Server Key" --scopes 'sessions.write' 'users.read' 'users.write' 'teams.read' 'teams.write' 'databases.read' 'databases.write' 'collections.read' 'collections.write' 'attributes.read' 'attributes.write' 'indexes.read' 'indexes.write' 'documents.read' 'documents.write' 'files.read' 'files.write' 'buckets.read' 'buckets.write' 'functions.read' 'functions.write' 'execution.read' 'execution.write' 'locale.read' 'avatars.read' 'health.read' 'providers.read' 'providers.write' 'messages.read' 'messages.write' 'topics.read' 'topics.write' 'subscribers.read' 'subscribers.write' 'targets.read' 'targets.write' 'rules.read' 'rules.write' 'migrations.read' 'migrations.write' 'vcs.read' 'vcs.write' 'assistant.read' 66 | $secret = ($create_key_response -split ' : ')[1].Trim() 67 | Write-Host $create_key_response 68 | Write-Host $secret 69 | 70 | # Pushing Server Key as env variable for cloud functions to use 71 | appwrite project create-variable --key APPWRITE_API_KEY --value $secret 72 | 73 | # Push endpoint as environment variable for functions to use (host.docker.internal used to access localhost from inside of script) 74 | appwrite project create-variable --key APPWRITE_ENDPOINT --value "http://host.docker.internal:80/v1" 75 | 76 | # Pushing the project's core defined in appwrite.json 77 | appwrite push collections 78 | appwrite push buckets 79 | 80 | # Uploading all the files on the server 81 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e10" --file "amber_profile_image.jpeg" 82 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e11" --file "classic_profile_image.jpeg" 83 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e12" --file "cream_profile_image.jpeg" 84 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e13" --file "forest_profile_image.jpeg" 85 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e14" --file "time_profile_image.jpeg" 86 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e15" --file "vintage_profile_image.jpeg" 87 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e16" --file "story.png" 88 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e17" --file "chapter.png" 89 | 90 | Write-Host "---- Appwrite Set Up complete (only functions left)----" 91 | 92 | 93 | Write-Host "Setting Up Livekit now ..." 94 | # Push Livekit credentials as env variables for functions to use 95 | while ($true) { 96 | $isLocalDeployment = Read-Host "Do you wish to opt for Livekit Cloud or Host Livekit locally? For Locally: y, For Cloud: n (y/n)" 97 | if ($isLocalDeployment -eq "y" -or $isLocalDeployment -eq "Y") { 98 | Write-Host "You chose to host Livekit locally." 99 | 100 | # check if Livekit server already running 101 | $PROCESS_ID = Get-Process | Where-Object { $_.Path -match "livekit-server" } | Select-Object -ExpandProperty Id 102 | if ($PROCESS_ID) { 103 | Stop-Process -Id $PROCESS_ID 104 | Write-Host "Livekit Server Already Running Terminating and Starting Again..." 105 | } else { 106 | Write-Host "Starting Livekit Server" 107 | } 108 | 109 | # Command to Start Livekit Server 110 | docker run -d --name livekit -p 7880:7880 livekit/livekit-server --dev --bind 0.0.0.0 111 | 112 | $livekitHostURL = "http://host.docker.internal:7880" 113 | $livekitSocketURL = "wss://host.docker.internal:7880" 114 | $livekitAPIKey = "devkey" 115 | $livekitAPISecret = "secret" 116 | break 117 | 118 | } elseif ($isLocalDeployment -eq "n" -or $isLocalDeployment -eq "N") { 119 | Write-Host "You chose to use Livekit Cloud." 120 | Write-Host "Please follow the steps on the Guide to Set Up Livekit Cloud, hence getting your self Livekit host url, socket url, API key, API secret" 121 | $livekitHostURL = Read-Host "Please Provide Livekit Host Url" 122 | $livekitSocketURL = Read-Host "Please Provide Livekit Socket Url" 123 | $livekitAPIKey = Read-Host "Please Provide Livekit API key" 124 | $livekitAPISecret = Read-Host "Please Provide Livekit API secret" 125 | break 126 | 127 | } else { 128 | Write-Host "Invalid input. Please enter 'y' for local or 'n' for cloud." 129 | } 130 | } 131 | 132 | # Push Livekit credentials as env variables for functions to use 133 | Write-Host "Pushing Livekit credentials as env variables if you need any changes do them in your Appwrite Resonate project's Global Env variables" 134 | appwrite project create-variable --key LIVEKIT_HOST --value $livekitHostURL 135 | appwrite project create-variable --key LIVEKIT_SOCKET_URL --value $livekitSocketURL 136 | appwrite project create-variable --key LIVEKIT_API_KEY --value $livekitAPIKey 137 | appwrite project create-variable --key LIVEKIT_API_SECRET --value $livekitAPISecret 138 | appwrite push functions --with-variables 139 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | OS="$(uname -s)" 4 | 5 | case "$OS" in 6 | Linux*) OS_TYPE=Linux;; 7 | Darwin*) OS_TYPE=Mac;; 8 | *) OS_TYPE="UNKNOWN:$OS" 9 | esac 10 | 11 | echo "Operating System: $OS_TYPE" 12 | 13 | echo "Installing Dependencies...." 14 | if [ "$OS_TYPE" = "Mac" ]; then 15 | # Check if Homebrew is installed 16 | if ! command -v brew &> /dev/null; then 17 | echo "Homebrew not found. Installing Homebrew..." 18 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 19 | else 20 | echo "Homebrew is already installed." 21 | fi 22 | brew install appwrite 23 | else 24 | curl -sL https://appwrite.io/cli/install.sh | bash 25 | fi 26 | 27 | 28 | docker run -it --add-host host.docker.internal:host-gateway --rm \ 29 | --volume /var/run/docker.sock:/var/run/docker.sock \ 30 | --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ 31 | --entrypoint="install" \ 32 | appwrite/appwrite:1.6.1 33 | 34 | projectId="resonate" 35 | 36 | 37 | # Remove previous Appwrite Cli data 38 | rm -rf ~/.appwrite | bash 39 | 40 | # Ask contributor account credentials 41 | while true; do 42 | appwrite login --endpoint "http://localhost:80/v1" 43 | if [ $? -eq 0 ]; then 44 | break 45 | else 46 | echo "Appwrite Login failed. Please try again." 47 | fi 48 | done 49 | 50 | 51 | echo "Starting resonate project set up...." 52 | # Get team id for project creation 53 | read -p "Please provide the team Id as instructed in the Resonate Set Up Guide:" teamId 54 | 55 | # Creating the project 56 | appwrite projects create --project-id resonate --name Resonate --team-id "$teamId" 57 | 58 | 59 | # Creating IOS and Android platforms 60 | appwrite projects create-platform --project-id "$projectId" --type flutter-android --key com.resonate.resonate --name Resonate 61 | appwrite projects create-platform --project-id "$projectId" --type flutter-ios --key com.resonate.resonate --name Resonate 62 | 63 | # Creating Server Key and Retrieving it from response 64 | create_key_response=$(appwrite projects create-key --project-id "$projectId" --name "Appwrite Server Key" --scopes 'sessions.write' 'users.read' 'users.write' 'teams.read' 'teams.write' 'databases.read' 'databases.write' 'collections.read' 'collections.write' 'attributes.read' 'attributes.write' 'indexes.read' 'indexes.write' 'documents.read' 'documents.write' 'files.read' 'files.write' 'buckets.read' 'buckets.write' 'functions.read' 'functions.write' 'execution.read' 'execution.write' 'locale.read' 'avatars.read' 'health.read' 'providers.read' 'providers.write' 'messages.read' 'messages.write' 'topics.read' 'topics.write' 'subscribers.read' 'subscribers.write' 'targets.read' 'targets.write' 'rules.read' 'rules.write' 'migrations.read' 'migrations.write' 'vcs.read' 'vcs.write' 'assistant.read') 65 | secret=$(echo "$create_key_response" | awk -F' : ' '/secret/ {print $2}') 66 | echo $create_key_response 67 | echo $secret 68 | 69 | 70 | # Pushing Server Key as env variable for cloud functions to use 71 | appwrite project create-variable --key APPWRITE_API_KEY --value "$secret" 72 | 73 | 74 | # Push endpoint as environment variable for functions to use (host.docker.internal used to access localhost from inside of script) 75 | appwrite project create-variable --key APPWRITE_ENDPOINT --value "http://host.docker.internal:80/v1" 76 | 77 | # Pushing the project's core defined in appwrite.json 78 | appwrite push collection 79 | appwrite push bucket 80 | 81 | # Uploading all the files on the server 82 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e10" --file "amber_profile_image.jpeg" 83 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e11" --file "classic_profile_image.jpeg" 84 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e12" --file "cream_profile_image.jpeg" 85 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e13" --file "forest_profile_image.jpeg" 86 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e14" --file "time_profile_image.jpeg" 87 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e15" --file "vintage_profile_image.jpeg" 88 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e16" --file "story.png" 89 | appwrite storage create-file --bucket-id "64a13095a4c87fd78bc6" --file-id "67012e19003d00f39e17" --file "chapter.png" 90 | 91 | echo "---- Appwrite Set Up complete (only functions left) ----" 92 | 93 | 94 | echo "Setting Up Livekit now ..." 95 | while true; do 96 | read -p "Do you wish to opt for Livekit Cloud or Host Livekit locally? For Locally: y, For Cloud: n (y/n)" isLocalDeployment 97 | if [[ $isLocalDeployment == "y" || $isLocalDeployment == "Y" ]]; then 98 | 99 | echo "You chose to host Livekit locally." 100 | 101 | # check if Livekit server already running 102 | PROCESS_ID=$(pgrep -f "livekit-server") 103 | if [ ! -z "$PROCESS_ID" ]; then 104 | kill $PROCESS_ID 105 | echo "Livekit Server Already Running Terminating and Starting Again..." 106 | else 107 | echo "Starting Livekit Server" 108 | fi 109 | 110 | # Command to Start Livekit Server 111 | docker run -d --name livekit -p 7880:7880 livekit/livekit-server --dev --bind 0.0.0.0 112 | 113 | livekitHostURL="http://host.docker.internal:7880" 114 | livekitSocketURL="wss://host.docker.internal:7880" 115 | livekitAPIKey="devkey" 116 | livekitAPISecret="secret" 117 | break 118 | 119 | elif [[ $isLocalDeployment == "n" || $isLocalDeployment == "N" ]]; then 120 | echo "You chose to use Livekit Cloud." 121 | echo "Please follow the steps on the Guide to Set Up Livekit Cloud, hence getting your self Livekit host url, socket url, API key, API secret" 122 | read -p "Please Provide Livekit Host Url: " livekitHostURL 123 | read -p "Please Provide Livekit Socket Url: " livekitSocketURL 124 | read -p "Please Provide Livekit API key: " livekitAPIKey 125 | read -p "Please Provide Livekit API secret: " livekitAPISecret 126 | break 127 | 128 | else 129 | echo "Invalid input. Please enter 'y' for local or 'n' for cloud." 130 | fi 131 | done 132 | 133 | 134 | # Push Livekit credentials as env variables for functions to use 135 | echo "Pushing Livekit credentials as env variables if you need any changes do them in your Appwrite Resonate projects Global Env variables" 136 | appwrite project create-variable --key LIVEKIT_HOST --value "$livekitHostURL" 137 | appwrite project create-variable --key LIVEKIT_SOCKET_URL --value "$livekitSocketURL" 138 | appwrite project create-variable --key LIVEKIT_API_KEY --value "$livekitAPIKey" 139 | appwrite project create-variable --key LIVEKIT_API_SECRET --value "$livekitAPISecret" 140 | appwrite push functions --with-variables 141 | 142 | echo "Many Congratulations Resonate Backend set up is complete !!! please further read the onboarding guide for connecting frontend to backend" -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Resonate-Backend", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": {} 6 | } 7 | -------------------------------------------------------------------------------- /story.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOSSIE-Org/Resonate-Backend/8e2d387c45202d23f179ed224e8d3f58112e4c44/story.png -------------------------------------------------------------------------------- /time_profile_image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOSSIE-Org/Resonate-Backend/8e2d387c45202d23f179ed224e8d3f58112e4c44/time_profile_image.jpeg -------------------------------------------------------------------------------- /vintage_profile_image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOSSIE-Org/Resonate-Backend/8e2d387c45202d23f179ed224e8d3f58112e4c44/vintage_profile_image.jpeg --------------------------------------------------------------------------------