diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8a4fe8e..09a1f96 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -130,7 +130,9 @@ jobs: build_platform: ${{ matrix.platform.name }} run: | aws s3 cp ./modules/gui/src-tauri/target/release/bundle/macos/tea.zip "s3://preview.gui.tea.xyz/release/tea_${{ steps.date.outputs.unix_seconds }}_$platform.zip" - + - uses: actions/setup-node@v3 + with: + node-version: 18 - name: Slack Notification run: ./.github/notify-slack.js env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f87c32a..3ac9726 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,19 @@ jobs: container: ${{ matrix.platform.container }} steps: - uses: actions/checkout@v3 - + - name: build platform output + id: build_platform + env: + platform: ${{ matrix.platform.name }} + run: | + EXTENSION=dmg + BUILD_PLATFORM=$(echo $platform | sed -e "s/darwin+//g" | sed -e "s/linux+//g") + [[ $BUILD_PLATFORM = "x86-64" ]] && BUILD_PLATFORM="x64" || BUILD_PLATFORM=$BUILD_PLATFORM + [[ $platform = "linux+x86-64" ]] && BUILD_PLATFORM="amd64" || BUILD_PLATFORM=$BUILD_PLATFORM + [[ $platform = "linux+x86-64" ]] && EXTENSION="deb" + echo "build_platform=$BUILD_PLATFORM" >> $GITHUB_OUTPUT + echo "extension=$EXTENSION" >> $GITHUB_OUTPUT + - name: build tauri for MacOS uses: teaxyz/setup@v0 if: startsWith(matrix.platform.name, 'darwin') @@ -70,7 +82,7 @@ jobs: aws s3 cp "./modules/gui/src-tauri/target/release/bundle/$extension/tea_0.1.0_$platform.$extension" \ "s3://preview.gui.tea.xyz/release/tea_gui_latest_$platform.$extension" aws s3 cp "./modules/gui/src-tauri/target/release/bundle/$extension/tea_0.1.0_$platform.$extension" \ - "s3://preview.gui.tea.xyz/release/tea_gui_$tag_$platform.$extension" + "s3://preview.gui.tea.xyz/release/tea_gui_${{ steps.tag.outputs.tag }}_$platform.$extension" - name: zip .app for MacOS if: startsWith(matrix.platform.name, 'darwin') @@ -87,7 +99,10 @@ jobs: aws s3 cp "./modules/gui/src-tauri/target/release/bundle/macos/tea.zip" \ "s3://preview.gui.tea.xyz/release/tea_gui_latest_$platform.zip" aws s3 cp "./modules/gui/src-tauri/target/release/bundle/macos/tea.zip" \ - "s3://preview.gui.tea.xyz/release/tea_gui_$tag_$platform.zip" + "s3://preview.gui.tea.xyz/release/tea_gui_${{ steps.tag.outputs.tag }}_$platform.zip" + - uses: actions/setup-node@v3 + with: + node-version: 18 - name: Slack Notification run: ./.github/notify-slack.js env: diff --git a/modules/gui/src-tauri/Cargo.toml b/modules/gui/src-tauri/Cargo.toml index 6576123..5320252 100644 --- a/modules/gui/src-tauri/Cargo.toml +++ b/modules/gui/src-tauri/Cargo.toml @@ -17,7 +17,7 @@ tauri-build = { version = "1.2.0", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.2.0", features = ["fs-read-dir", "http-all", "shell-all", "window-all"] } +tauri = { version = "1.2.0", features = ["fs-create-dir", "fs-read-dir", "fs-read-file", "fs-write-file", "http-all", "path-all", "shell-all", "window-all"] } uuid = "1.2.1" futures = "0.3" diff --git a/modules/gui/src-tauri/tauri.conf.json b/modules/gui/src-tauri/tauri.conf.json index cdcbf2b..dccd905 100644 --- a/modules/gui/src-tauri/tauri.conf.json +++ b/modules/gui/src-tauri/tauri.conf.json @@ -15,7 +15,7 @@ "http": { "all": true, "request": true, - "scope": ["https://api.tea.xyz/v1/*", "https://github.com/*", "https://app.tea.xyz/*"] + "scope": ["https://api.tea.xyz/v1/*", "https://github.com/*", "http://localhost:3000/v1/*", "https://app.tea.xyz/*"] }, "shell": { "all": true, @@ -36,6 +36,11 @@ "name": "list-packages", "cmd": "ls", "args": ["-R ~/.tea/tea.xyz/var/www | grep 'xz\\|gz'"] + }, + { + "name": "open", + "cmd": "open", + "args": ["-a iterm"] } ], "sidecar": false @@ -74,10 +79,15 @@ }, "fs": { "readDir": true, + "createDir": true, + "writeFile": true, + "readFile": true, "scope": [ - "$HOME/.tea/*", - "$APPDATA/*" + "$HOME/.tea/*" ] + }, + "path": { + "all": true } }, "bundle": { diff --git a/modules/gui/src/components/NavBar/NavBar.svelte b/modules/gui/src/components/NavBar/NavBar.svelte index 947cfa5..f818e45 100644 --- a/modules/gui/src/components/NavBar/NavBar.svelte +++ b/modules/gui/src/components/NavBar/NavBar.svelte @@ -3,6 +3,7 @@ import { searchStore } from '$libs/stores'; import SearchInput from '@tea/ui/SearchInput/SearchInput.svelte'; import Button from '@tea/ui/Button/Button.svelte'; + import ProfileNavButton from './ProfileNavButton.svelte'; import { beforeUpdate } from 'svelte'; @@ -70,12 +71,7 @@ {/each} diff --git a/modules/gui/src/components/NavBar/ProfileNavButton.svelte b/modules/gui/src/components/NavBar/ProfileNavButton.svelte new file mode 100644 index 0000000..1cb3e5a --- /dev/null +++ b/modules/gui/src/components/NavBar/ProfileNavButton.svelte @@ -0,0 +1,37 @@ + + +{#if user} + +
+ profile +
@{user.login}
+
+
+{:else} + +
+
+ profile +
+
Login
+
+{/if} diff --git a/modules/gui/src/components/ProfileBanner/ProfileBanner.svelte b/modules/gui/src/components/ProfileBanner/ProfileBanner.svelte index cdd3d36..3cf7154 100644 --- a/modules/gui/src/components/ProfileBanner/ProfileBanner.svelte +++ b/modules/gui/src/components/ProfileBanner/ProfileBanner.svelte @@ -1,23 +1,36 @@ -
-
- profile -
-
-

Authenticated with GitHub

-

-

@Username

-
-
-
-

- Country: Germany
Wallet: - Connect Now -

+{#if user} +
+
+ profile +
+
+

Authenticated with GitHub

+

+

@{user.login}

+
+
+
+

+ Country: {user?.country}
Wallet: + {#if user.wallet} + {user.wallet} + {:else} + Connect Now + {/if} +

+
-
-
+
+{/if} diff --git a/modules/gui/src/libs/api/mock.ts b/modules/gui/src/libs/api/mock.ts index d4172f7..45beba2 100644 --- a/modules/gui/src/libs/api/mock.ts +++ b/modules/gui/src/libs/api/mock.ts @@ -11,6 +11,8 @@ import { PackageStates } from '../types'; import { loremIpsum } from 'lorem-ipsum'; import _ from 'lodash'; +export const apiBaseUrl = 'https://api.tea.xyz/v1'; + const packages: Package[] = [ { slug: 'mesonbuild_com', @@ -323,6 +325,24 @@ export async function getCategorizedPackages(): Promise { ]; } +export async function getDeviceAuth(deviceId: string): Promise { + // const data = await get(`/auth/device/${deviceId}`); + return { + status: 'SUCCESS', + user: { + developer_id: 'xxx', + name: 'Neil paul Molina', + login: 'getneil', + avatar_url: 'https://avatars.githubusercontent.com/u/7913978?v=4', + created_at: 'xxx', + updated_at: 'xxx', + country: 'germany', + wallet: 'wallet' + }, + key: 'xxx' + }; +} + export async function getPackageBottles(name: string): Promise { return [ { name, platform: 'darwin', arch: 'aarch64', version: '3.39.4' }, @@ -335,3 +355,7 @@ export async function getPackageBottles(name: string): Promise { { name, platform: 'linux', arch: 'x86-64', version: '3.40.0' } ]; } + +export async function registerDevice(): Promise { + return 'uuid1234'; +} diff --git a/modules/gui/src/libs/api/tauri.ts b/modules/gui/src/libs/api/tauri.ts index b034e55..4e38a07 100644 --- a/modules/gui/src/libs/api/tauri.ts +++ b/modules/gui/src/libs/api/tauri.ts @@ -14,20 +14,25 @@ import { getClient } from '@tauri-apps/api/http'; // import { invoke } from '@tauri-apps/api'; import { Command } from '@tauri-apps/api/shell'; import { readDir, BaseDirectory } from '@tauri-apps/api/fs'; -import type { Package, Review, AirtablePost, Bottle } from '@tea/ui/types'; -import type { GUIPackage, Course, Category } from '../types'; + +import type { Package, Review, AirtablePost, Developer, Bottle } from '@tea/ui/types'; +import type { GUIPackage, Course, Category, AuthStatus } from '../types'; + import * as mock from './mock'; import { PackageStates } from '../types'; -const base = 'https://api.tea.xyz/v1'; +export const apiBaseUrl = 'https://api.tea.xyz/v1'; +// const apiBaseUrl = 'http://localhost:3000/v1'; async function get(path: string, query?: { [key: string]: string }) { + console.log('path', path); const client = await getClient(); - const uri = join(base, path); + const uri = join(apiBaseUrl, path); + console.log('uri:', uri); const { data } = await client.get(uri.toString(), { headers: { - Authorization: 'public', // TODO: figure out why req w/o Authorization does not work - 'cache-control': 'no-cache' + Authorization: 'public' // TODO: figure out why req w/o Authorization does not work + // 'cache-control': 'no-cache' }, query: query || {} }); @@ -164,6 +169,17 @@ export async function getCategorizedPackages(): Promise { return categories; } +type DeviceAuth = { + status: AuthStatus; + user: Developer; + key: string; +}; + +export async function getDeviceAuth(deviceId: string): Promise { + const data = await get(`/auth/device/${deviceId}`); + return data; +} + export async function getPackageBottles(packageName: string): Promise { console.log('getting bottles for ', packageName); const client = await getClient(); @@ -172,3 +188,8 @@ export async function getPackageBottles(packageName: string): Promise console.log('got bottles', data); return data; } + +export async function registerDevice(): Promise { + const { deviceId } = await get<{ deviceId: string }>('/auth/registerDevice'); + return deviceId; +} diff --git a/modules/gui/src/libs/stores.ts b/modules/gui/src/libs/stores.ts index 316139a..2fcb107 100644 --- a/modules/gui/src/libs/stores.ts +++ b/modules/gui/src/libs/stores.ts @@ -5,6 +5,7 @@ import type { Package, Review, AirtablePost } from '@tea/ui/types'; import type { GUIPackage } from '$libs/types'; import { getPackages, getFeaturedPackages, getPackageReviews, getAllPosts } from '@api'; +import initAuthStore from './stores/auth'; export const backLink = writable('/'); @@ -169,3 +170,5 @@ function initSearchStore() { } export const searchStore = initSearchStore(); + +export const authStore = initAuthStore(); diff --git a/modules/gui/src/libs/stores/auth.ts b/modules/gui/src/libs/stores/auth.ts new file mode 100644 index 0000000..739a1c9 --- /dev/null +++ b/modules/gui/src/libs/stores/auth.ts @@ -0,0 +1,115 @@ +import { writable } from 'svelte/store'; +import { BaseDirectory, createDir, readTextFile, writeTextFile } from '@tauri-apps/api/fs'; +import { join } from '@tauri-apps/api/path'; +import { getDeviceAuth, registerDevice } from '@api'; +import type { Developer } from '@tea/ui/types'; + +const basePath = '.tea/tea.xyz/gui'; +interface Session { + device_id?: string; + key?: string; + user?: Developer; +} + +export default function initAuthStore() { + const session = writable({}); + let pollLoop = 0; + + const deviceIdStore = writable(''); + let deviceId = ''; + + initSession().then((sess) => { + if (sess) { + session.set(sess); + deviceIdStore.set(sess.device_id!); + deviceId = sess.device_id!; + } + }); + + let timer: NodeJS.Timer | null; + + async function updateSession(data: Session) { + const localSession = { + device_id: deviceId, + key: data.key, + user: data.user + }; + saveLocallySessionData(localSession); + session.set(localSession); + } + + async function pollSession() { + if (!timer) { + timer = setInterval(async () => { + pollLoop++; + try { + const data = await getDeviceAuth(deviceId); + console.log('dd', deviceId, data); + if (data.status === 'SUCCESS') { + updateSession({ + key: data.key, + user: data.user + }); + timer && clearInterval(timer); + timer = null; + } + } catch (error) { + console.error(error); + } + + if (pollLoop > 20 && timer) { + clearInterval(timer); + pollLoop = 0; + timer = null; + } + }, 2000); + } + } + + return { + deviceId, + deviceIdStore, + subscribe: (cb: (u: Developer) => void) => { + return session.subscribe((v) => v?.user && cb(v.user)); + }, + pollSession + }; +} + +const initSession = async (): Promise => { + await createDir(basePath, { + dir: BaseDirectory.Home, + recursive: true + }); + const session = await getLocalSessionData(); + return session; +}; + +const getLocalSessionData = async (): Promise => { + const sessionFilePath = await join(basePath, 'tmp.dat'); + let data: Session; + try { + const encryptedData = await readTextFile(sessionFilePath, { + dir: BaseDirectory.Home + }); + // TODO: decrypt then return + data = JSON.parse(encryptedData || '{}'); + } catch (error) { + console.error(error); + const deviceId = await registerDevice(); + data = { + device_id: deviceId + }; + await saveLocallySessionData(data); + } + + return data; +}; + +const saveLocallySessionData = async (data: Session) => { + const sessionFilePath = await join(basePath, 'tmp.dat'); + // TODO: encrypt first + await writeTextFile(sessionFilePath, JSON.stringify(data), { + dir: BaseDirectory.Home + }); +}; diff --git a/modules/gui/src/libs/types.ts b/modules/gui/src/libs/types.ts index b4617f4..dd9f8be 100644 --- a/modules/gui/src/libs/types.ts +++ b/modules/gui/src/libs/types.ts @@ -29,3 +29,10 @@ export type Category = { cta_label: string; packages: GUIPackage[]; }; + +export enum AuthStatus { + UNKNOWN = 'UNKNOWN', + PENDING = 'PENDING', + SUCCESS = 'SUCCESS', + FAILED = 'FAILED' +} diff --git a/modules/gui/src/routes/+page.svelte b/modules/gui/src/routes/+page.svelte index cb33f10..78f5400 100644 --- a/modules/gui/src/routes/+page.svelte +++ b/modules/gui/src/routes/+page.svelte @@ -9,6 +9,7 @@ import News from '$components/News/News.svelte'; import CategorizedPackages from '$components/CategorizedPackages/CategorizedPackages.svelte'; backLink.set(''); + console.log('test', window.location);
diff --git a/modules/gui/src/routes/profile/+page.svelte b/modules/gui/src/routes/profile/+page.svelte index 21855de..149a3ec 100644 --- a/modules/gui/src/routes/profile/+page.svelte +++ b/modules/gui/src/routes/profile/+page.svelte @@ -11,7 +11,6 @@
PROFILE -
diff --git a/modules/gui/static/images/github.png b/modules/gui/static/images/github.png new file mode 100644 index 0000000..3a1bfa4 Binary files /dev/null and b/modules/gui/static/images/github.png differ diff --git a/modules/ui/src/types.ts b/modules/ui/src/types.ts index 606cbf2..afc7524 100644 --- a/modules/ui/src/types.ts +++ b/modules/ui/src/types.ts @@ -35,6 +35,15 @@ export type AirtablePost = { tags: string[]; }; +export type Developer = { + developer_id: string; + avatar_url?: string; + name: string; + login: string; + country?: string; + wallet?: string; +}; + export type Bottle = { name: string; platform: string;