Skip to content

Commit 6ea05e6

Browse files
committed
setup oauth, signup, session etc for local
- lots of things still moving around, now we have cli, shared and app in src. One of which holds sveltekit app - only half baked still, but getting all the key pieces for auth via auth0, account creation, adding connection, and also locking down different deployments (ie may want to run a tenant and lock down what options there are for auth ... ) - things a little confused between using oauth2 for login into imtala, and also using it to access 3rd party apis, will properly tease this apart soon
1 parent b5833d9 commit 6ea05e6

File tree

21 files changed

+691
-147
lines changed

21 files changed

+691
-147
lines changed

.github/workflows/push-master.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
- master
55

66
jobs:
7-
cache-and-install:
7+
publish-cli:
88
runs-on: ubuntu-latest
99

1010
steps:

packages/imtala/.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ node_modules
66
.env
77
.env.*
88
!.env.example
9-
*.tgz
9+
*.tgz
10+
/lib

packages/imtala/bin/cli.js

+1-24
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,2 @@
11
#! /usr/bin/env node
2-
import { Command } from 'commander'
3-
import {handler} from '../build/handler.js';
4-
const program = new Command();
5-
import http from 'http'
6-
7-
program
8-
.name('imtala')
9-
.description('Graphql powered automation tooling')
10-
.version('0.8.0');
11-
12-
program.command('serve')
13-
.description('Start the imtala server')
14-
.option('-p, --port <number>', 'port', 3000)
15-
.action((options) => {
16-
const port = options.port;
17-
18-
const server = http.createServer(handler);
19-
20-
server.listen(port, () => {
21-
console.log(`Imtala running at http://localhost:${port}/`);
22-
});
23-
});
24-
25-
program.parse();
2+
import '../lib/cli/index.js'

packages/imtala/package.json

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
22
"name": "@imtala/cli",
3-
"version": "2.0.0-next.2",
3+
"version": "2.0.0-next.3",
44
"devDependencies": {
55
"@playwright/test": "^1.25.0",
66
"@sveltejs/adapter-auto": "next",
77
"@sveltejs/kit": "next",
8+
"@types/node": "^18.8.5",
89
"@typescript-eslint/eslint-plugin": "^5.27.0",
910
"@typescript-eslint/parser": "^5.27.0",
1011
"eslint": "^8.16.0",
@@ -22,20 +23,24 @@
2223
"type": "module",
2324
"dependencies": {
2425
"@sveltejs/adapter-node": "1.0.0-next.96",
25-
"commander": "^9.4.1"
26+
"commander": "^9.4.1",
27+
"esbuild": "^0.15.11"
2628
},
2729
"bin": {
2830
"imtala": "bin/cli.js"
2931
},
3032
"scripts": {
3133
"dev": "vite dev",
32-
"build": "vite build",
34+
"build:app": "echo 'building app' && vite build",
35+
"build:cli": "echo 'building cli' && esbuild src/shared/* src/cli/* --outdir=lib --platform=node",
36+
"build": "pnpm build:app && pnpm build:cli",
3337
"preview": "vite preview",
3438
"test": "playwright test",
3539
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
3640
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
3741
"lint": "prettier --plugin-search-dir . --check . && eslint .",
3842
"format": "prettier --plugin-search-dir . --write .",
39-
"release": "pnpm publish"
43+
"bump-version": "pnpm version prerelease --preid next",
44+
"release": "pnpm publish --tag next"
4045
}
4146
}

packages/imtala/src/app.html

-12
This file was deleted.
File renamed without changes.

packages/imtala/src/app/app.html

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8" />
6+
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
7+
<meta name="viewport" content="width=device-width" />
8+
%sveltekit.head%
9+
</head>
10+
11+
<body>
12+
<style>
13+
:root {
14+
--pink-8: #2e6874;
15+
--pink-7: #423d83;
16+
--pink-6: #271a26;
17+
--action-remove: #977051;
18+
--pink-4: #38121f;
19+
--pink-3: #ca8174;
20+
--pink-2: #d8ae77;
21+
--pink-1: #d8e365;
22+
--lime-1: #d8e365;
23+
--lime-2: #c3d877;
24+
--lime-3: #98ca74;
25+
--lime-4: #81bd6f;
26+
--lime-5: #6baf67;
27+
--lime-6: #4f9765;
28+
--lime-7: #3d836b;
29+
--lime-8: #2e6874;
30+
--lime-9: #2e6874;
31+
--light-1: #dfe78a;
32+
--light-2: #e6ebb4;
33+
--light-3: #eff1d5;
34+
}
35+
36+
body {
37+
font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace;
38+
background-color: black;
39+
color: white;
40+
}
41+
42+
main {
43+
width: 100%
44+
}
45+
46+
input[type='submit'] {
47+
cursor: pointer;
48+
color: var(--lime-1);
49+
border: none;
50+
width:auto;
51+
margin: 1em;
52+
padding: 0;
53+
}
54+
55+
a,
56+
input[type='submit'] {
57+
text-decoration: none;
58+
color: var(--lime-1);
59+
}
60+
a:visited {
61+
color: var(--lime-5);
62+
}
63+
64+
a:hover,
65+
input[type='submit']:hover {
66+
background-color: var(--lime-8);
67+
}
68+
69+
input,
70+
select,
71+
textarea {
72+
font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace;
73+
outline: none;
74+
background-color: black;
75+
padding: 1em;
76+
margin: 1em;
77+
width: 30em;
78+
79+
border: 1px solid var(--lime-2);
80+
font-size: 14px;
81+
color: var(--lime-2);
82+
}
83+
84+
label {
85+
display: block;
86+
margin: 0 1em;
87+
/* width: 100%; */
88+
}
89+
90+
91+
@media only screen and (min-width: 400px) {
92+
main {
93+
width: 90%;
94+
min-width: 600px;
95+
max-width: 800px;
96+
margin: auto;
97+
border-left: 1px solid white;
98+
padding-left: 20px;
99+
}
100+
}
101+
</style>
102+
<div>%sveltekit.body%</div>
103+
</body>
104+
105+
</html>

packages/imtala/src/app/lib/store.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import store from '../../shared/store'
2+
3+
await store.init()
4+
5+
export default store.getStore

packages/imtala/src/app/lib/user.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { Cookies } from '@sveltejs/kit'
2+
import getStore from './store'
3+
4+
export const getUserFromSession = async (cookies: Cookies) => {
5+
const sessionKey = cookies.get('imtala_session_id')
6+
if (!sessionKey) return;
7+
8+
const {session: sessionStore, user: userStore} = await getStore()
9+
10+
const session = await sessionStore.get(sessionKey)
11+
12+
if (!session) return;
13+
14+
return userStore.get(session.userId)
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { error } from '@sveltejs/kit';
2+
3+
import type { PageServerLoad } from './$types';
4+
import {getUserFromSession} from '../lib/user'
5+
import getStore from '../lib/store'
6+
7+
8+
export const load: PageServerLoad = async ({ cookies }) => {
9+
const {connection: connectionStore} = await getStore()
10+
const user = await getUserFromSession(cookies);
11+
return {
12+
user,
13+
connections: user && await (await connectionStore.list()).filter(connection => connection.userId === user.userId) || []
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<script lang="ts">
2+
import type { PageData } from './$types';
3+
export let data: PageData;
4+
</script>
5+
6+
<main>
7+
8+
{#if !data.user}
9+
<h1>Welcome to Imtala</h1>
10+
11+
Get started by <a href="/connections/new">creating a connection</a>, or
12+
<a href="/auth/login">login</a>
13+
{:else}
14+
<h1>Hi {data.user.fullName.split(' ')[0]}, Welcome to Imtala</h1>
15+
<h2>Connections</h2>
16+
<ul>
17+
{#each data.connections as connection}
18+
<li>{connection.name}</li>
19+
{/each}
20+
</ul>
21+
{/if}
22+
23+
<p>features coming soon</p>
24+
25+
<h2>Todo</h2>
26+
<ul>
27+
<li>Tenent and session management</li>
28+
</ul>
29+
</main>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import getStore from '../../../../lib/store';
2+
import { randomBytes } from 'crypto';
3+
4+
import type { PageServerLoad } from './$types'
5+
import { redirect, error } from '@sveltejs/kit';
6+
7+
const createId = (bytes: number): Promise<string> => new Promise((res, rej) => {
8+
randomBytes(bytes, (err, buf) => {
9+
if (err) return rej(err)
10+
11+
res(buf.toString('base64url'))
12+
})
13+
})
14+
15+
export const load: PageServerLoad = async ({ params, cookies, url }) => {
16+
const { providerName } = params;
17+
const {
18+
connection: connectionStore,
19+
authMethod: authMethodStore,
20+
user: userStore,
21+
session: sessionStore,
22+
authProvider: authProviderStore
23+
} = await getStore()
24+
25+
const authProvider = await authProviderStore.get(providerName)
26+
27+
if (!authProvider) {
28+
throw error(403, 'forbidden')
29+
}
30+
31+
const accessTokenResponse = await fetch('https://github.com/login/oauth/access_token', {
32+
method: 'POST',
33+
headers: {
34+
'Accept': 'application/json',
35+
['Content-Type']: 'application/json',
36+
},
37+
body: JSON.stringify({
38+
client_id: authProvider.clientId,
39+
client_secret: authProvider.clientSecret,
40+
code: url.searchParams.get('code'),
41+
response_type: 'code',
42+
grant_type: 'authorization_code',
43+
redirect_uri: authProvider.callbackUrl
44+
})
45+
})
46+
47+
48+
const accessTokenResponsePayload = await accessTokenResponse.json();
49+
const token = accessTokenResponsePayload.access_token;
50+
51+
const ghUserResponse = await fetch('https://api.github.com/user', {
52+
method: 'GET',
53+
headers: {
54+
Authorization: `Bearer ${token}`
55+
}
56+
})
57+
const userData = await ghUserResponse.json()
58+
59+
const authMethods = await authMethodStore.list();
60+
const authMethod = authMethods
61+
.filter(authMethod => authMethod.authProviderType === providerName)
62+
.find(authMethod => authMethod.identifier === userData.id)
63+
64+
if (authMethod) {
65+
// login flow
66+
const sessionId = await createId(24)
67+
68+
await sessionStore.set(sessionId, {
69+
userId: authMethod.userId,
70+
sessionId
71+
})
72+
73+
cookies.set('imtala_session_id', sessionId, { path: '/' })
74+
75+
throw redirect(303, '/')
76+
}
77+
78+
// signup & create connection flow
79+
80+
const connectionId = await createId(24)
81+
const userId = await createId(24)
82+
const authMethodId = await createId(24)
83+
const sessionId = await createId(24)
84+
85+
86+
await authMethodStore.set(userData.id, {
87+
identifier: userData.id,
88+
authProviderType: 'github',
89+
userId: userId,
90+
token
91+
})
92+
93+
await connectionStore.set(connectionId, {
94+
name: providerName,
95+
connectionId,
96+
userId,
97+
authMethodId
98+
})
99+
100+
await userStore.set(userId, {
101+
fullName: userData.name,
102+
userId
103+
})
104+
105+
await sessionStore.set(sessionId, {
106+
userId: userId,
107+
sessionId
108+
})
109+
110+
cookies.set('imtala_session_id', sessionId, { path: '/' })
111+
112+
throw redirect(303, '/')
113+
114+
}

0 commit comments

Comments
 (0)