mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-12 08:45:38 +02:00
✨ feat: Add auth flow (#146)
This adds a simple way to incorporate a centralized authentication flow. The idea is to have the user, API and SSH (for machine authentication) all in one place using `openauthjs` + `SST` We also have a database now :) > We are using InstantDB as it allows us to authenticate a use with just the email. Plus it is super simple simple to use _of course after the initial fumbles trying to design the db and relationships_
This commit is contained in:
@@ -1,87 +0,0 @@
|
||||
# How to Deploy Your Own MoQ Relay on a Server
|
||||
|
||||
This guide will walk you through the steps to deploy your own MoQ relay on a server.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Server Requirements:**
|
||||
- Ensure port 443 is open for both TCP and UDP (`:443/udp & :443/tcp`).
|
||||
- The server should have a minimum of **4GB RAM** and **2 vCPUs**.
|
||||
- Supports ARM or AMD64 architecture.
|
||||
|
||||
2. **Software Requirements:**
|
||||
- Docker and `docker-compose` must be installed on the server. You can use [this installation script](https://github.com/docker/docker-install) for Docker.
|
||||
- Git must be installed to clone the necessary repository.
|
||||
|
||||
3. **Certificates:**
|
||||
- You will need private and public certificates. It is recommended to use certificates from a trusted CA rather than self-signed certificates.
|
||||
|
||||
## Installation Steps
|
||||
|
||||
### Step 1: Clone the Repository
|
||||
|
||||
Clone the `kixelated/moq-rs` repository to your local machine:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/kixelated/moq-rs moq
|
||||
```
|
||||
|
||||
### Step 2: Verify Port Availability
|
||||
|
||||
Check if port 443 is already in use on your server:
|
||||
|
||||
```bash
|
||||
sudo netstat -tulpn | grep ':443' | grep LISTEN
|
||||
```
|
||||
or
|
||||
```bash
|
||||
sudo lsof -i -P -n | grep LISTEN | grep 443
|
||||
```
|
||||
|
||||
If you find any processes using port 443, consider terminating them.
|
||||
|
||||
### Step 3: Configure Ports
|
||||
|
||||
Navigate to the cloned directory and edit the Docker compose file to use port 443:
|
||||
|
||||
```bash
|
||||
cd moq
|
||||
vim docker-compose.yml
|
||||
```
|
||||
|
||||
Change the ports section from lines 34 to 35 to:
|
||||
|
||||
```yaml
|
||||
ports:
|
||||
- "443:443"
|
||||
- "443:443/udp"
|
||||
```
|
||||
|
||||
### Step 4: Prepare Certificates
|
||||
|
||||
Copy your generated certificates into the `moq/dev` directory and rename them:
|
||||
|
||||
```bash
|
||||
cp cert.pem moq/dev/localhost.crt
|
||||
cp key.pem moq/dev/localhost.key
|
||||
```
|
||||
|
||||
### Step 5: Start Docker Instances
|
||||
|
||||
Ensure you are in the root directory of the `moq` project, then start the Docker containers:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Step 6: Link Domain to Server IP
|
||||
|
||||
Configure your DNS settings to connect your server's IP address to your domain:
|
||||
|
||||
```
|
||||
Record Type: A
|
||||
Subdomain: relay.fst.so
|
||||
IP Address: xx.xxx.xx.xxx
|
||||
```
|
||||
|
||||
Congratulations, your MoQ server is now set up! You can verify its functionality by using the [MoQ Checker](https://nestri.pages.dev/moq/checker).
|
||||
68
infra/api.ts
68
infra/api.ts
@@ -1,22 +1,52 @@
|
||||
import { isPermanentStage } from "./stage";
|
||||
import { domain } from "./dns";
|
||||
import { secret } from "./secrets"
|
||||
|
||||
//TODO: Use this instead of wrangler
|
||||
// export const api = new sst.cloudflare.Worker("apiApi", {
|
||||
// url: true,
|
||||
// handler: "packages/api/src/index.ts",
|
||||
// // live: true,
|
||||
// });
|
||||
sst.Linkable.wrap(random.RandomString, (resource) => ({
|
||||
properties: {
|
||||
value: resource.result,
|
||||
},
|
||||
}));
|
||||
|
||||
if (!isPermanentStage) {
|
||||
new sst.x.DevCommand("apiDev", {
|
||||
dev: {
|
||||
command: "bun run dev",
|
||||
directory: "packages/api",
|
||||
autostart: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
export const authFingerprintKey = new random.RandomString(
|
||||
"AuthFingerprintKey",
|
||||
{
|
||||
length: 32,
|
||||
},
|
||||
);
|
||||
|
||||
// export const outputs = {
|
||||
// api: api.url
|
||||
// }
|
||||
export const urls = new sst.Linkable("Urls", {
|
||||
properties: {
|
||||
api: "https://api." + domain,
|
||||
auth: "https://auth." + domain,
|
||||
},
|
||||
});
|
||||
|
||||
export const kv = new sst.cloudflare.Kv("CloudflareAuthKV")
|
||||
|
||||
export const auth = new sst.cloudflare.Worker("Auth", {
|
||||
link: [
|
||||
kv,
|
||||
urls,
|
||||
authFingerprintKey,
|
||||
secret.InstantAdminToken,
|
||||
secret.InstantAppId,
|
||||
secret.LoopsApiKey
|
||||
],
|
||||
handler: "./packages/functions/src/auth.ts",
|
||||
url: true,
|
||||
domain: "auth." + domain
|
||||
});
|
||||
|
||||
export const api = new sst.cloudflare.Worker("Api", {
|
||||
link: [
|
||||
urls,
|
||||
],
|
||||
url: true,
|
||||
handler: "./packages/functions/src/api/index.ts",
|
||||
domain: "api." + domain
|
||||
})
|
||||
|
||||
export const outputs = {
|
||||
auth: auth.url,
|
||||
api: api.url
|
||||
}
|
||||
10
infra/cli.ts
Normal file
10
infra/cli.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { auth, urls } from "./api"
|
||||
|
||||
|
||||
// export const cmd = new sst.x.DevCommand("Cmd", {
|
||||
// link: [urls, auth],
|
||||
// dev: {
|
||||
// autostart: true,
|
||||
// command: "cd packages/cmd && go run main.go"
|
||||
// }
|
||||
// })
|
||||
9
infra/dns.ts
Normal file
9
infra/dns.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export const domain =
|
||||
{
|
||||
production: "prod.nestri.io", //temporary use until we go into the real production
|
||||
dev: "dev.nestri.io",
|
||||
}[$app.stage] || $app.stage + ".dev.nestri.io";
|
||||
|
||||
export const zone = cloudflare.getZoneOutput({
|
||||
name: "nestri.io",
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
// export const domain =
|
||||
// {
|
||||
// production: "fst.so",
|
||||
// dev: "dev.fst.so",
|
||||
// }[$app.stage] || $app.stage + ".dev.fst.so";
|
||||
|
||||
// export const zone = cloudflare.getZoneOutput({
|
||||
// name: "fst.so",
|
||||
// });
|
||||
@@ -1,22 +0,0 @@
|
||||
import { resolve as pathResolve } from "node:path";
|
||||
import { readFileSync as readFile } from "node:fs";
|
||||
//Copy your (known) ssh public key to the remote machine
|
||||
//ssh-copy-id "-p $port" user@host
|
||||
|
||||
const domain = "fst.so"
|
||||
const ips = ["95.216.29.238"]
|
||||
|
||||
// Get the hosted zone
|
||||
const zone = aws.route53.getZone({ name: domain });
|
||||
|
||||
// Create an A record
|
||||
const record = new aws.route53.Record("Relay DNS Records", {
|
||||
zoneId: zone.then(zone => zone.zoneId),
|
||||
type: "A",
|
||||
name: `relay.${domain}`,
|
||||
ttl: 300,
|
||||
records: ips,
|
||||
});
|
||||
|
||||
// Export the URL
|
||||
export const url = $interpolate`https://${record.name}`;
|
||||
@@ -1,5 +1,7 @@
|
||||
export const secret = {
|
||||
CloudflareAccountIdSecret: new sst.Secret("CloudflareAccountId"),
|
||||
};
|
||||
|
||||
export const allSecrets = Object.values(secret);
|
||||
InstantAdminToken: new sst.Secret("InstantAdminToken"),
|
||||
InstantAppId: new sst.Secret("InstantAppId"),
|
||||
LoopsApiKey: new sst.Secret("LoopsApiKey")
|
||||
};
|
||||
|
||||
export const allSecrets = Object.values(secret);
|
||||
79
infra/www.ts
79
infra/www.ts
@@ -1,79 +0,0 @@
|
||||
//Deploys the website to cloudflare pages under the domain nestri.io (redirects all requests to www.nestri.io to avoid duplicate content)
|
||||
|
||||
import { isPermanentStage } from "./stage";
|
||||
|
||||
export const www = new cloudflare.PagesProject("www", {
|
||||
name: "nestri",
|
||||
accountId: "8405b2acb6746935b975bc2cfcb5c288",
|
||||
productionBranch: "main",
|
||||
buildConfig: {
|
||||
rootDir: "apps/www",
|
||||
buildCommand: "bun run build",
|
||||
destinationDir: "dist"
|
||||
},
|
||||
deploymentConfigs: {
|
||||
production: {
|
||||
compatibilityFlags: ["nodejs_compat"]
|
||||
},
|
||||
preview: {
|
||||
compatibilityFlags: ["nodejs_compat"]
|
||||
}
|
||||
},
|
||||
source: {
|
||||
type: "github",
|
||||
config: {
|
||||
owner: "nestriness",
|
||||
deploymentsEnabled: true,
|
||||
productionBranch: "main",
|
||||
repoName: "nestri",
|
||||
productionDeploymentEnabled: true,
|
||||
prCommentsEnabled: true,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//TODO: Maybe handle building Qwik ourselves? This prevents us from relying on CF too much, we are open-source anyway 🤷🏾♂️
|
||||
//TODO: Add a local dev server for Qwik that can be linked with whatever we want
|
||||
//TODO: Link the www PageRule with whatever we give to the local dev server
|
||||
if (!isPermanentStage) {
|
||||
new sst.x.DevCommand("www", {
|
||||
dev: {
|
||||
command: "bun run dev",
|
||||
directory: "apps/www",
|
||||
autostart: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// //This creates a resource that can be accessed by itself
|
||||
// new sst.Linkable.wrap(cloudflare.PageRule, (resource) => ({
|
||||
// // these properties will be available when linked
|
||||
// properties: {
|
||||
// arn: resource.urn
|
||||
// }
|
||||
// }))
|
||||
// //And then you call your linkable resource like this:
|
||||
// // const www = cloudflare.PageRule("www", {})
|
||||
|
||||
// //this creates a linkable resource that can be linked to other resources
|
||||
// export const linkable2 = new sst.Linkable("ExistingResource", {
|
||||
// properties: {
|
||||
// arn: "arn:aws:s3:::nestri-website-artifacts-prod-nestri-io-01h70zg50qz5z"
|
||||
// },
|
||||
// include: [
|
||||
// sst.aws.permission({
|
||||
// actions: ["s3:*"],
|
||||
// resources: ["arn:aws:s3:::nestri-website-artifacts-prod-nestri-io-01h70zg50qz5z"]
|
||||
// }),
|
||||
// sst.cloudflare.binding({
|
||||
// type: "r2BucketBindings",
|
||||
// properties: {
|
||||
// bucketName: "nestri-website-artifacts-prod-nestri-io-01h70zg50qz5z",
|
||||
// }
|
||||
// })
|
||||
// ]
|
||||
// })
|
||||
|
||||
export const outputs = {
|
||||
www: www.subdomain,
|
||||
};
|
||||
Reference in New Issue
Block a user