mirror of
https://github.com/ivabus/gui
synced 2025-06-07 15:50:27 +03:00
commit
fa418cb82b
16 changed files with 290 additions and 38 deletions
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
|
@ -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:
|
||||
|
|
19
.github/workflows/release.yml
vendored
19
.github/workflows/release.yml
vendored
|
@ -20,6 +20,18 @@ 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
|
||||
|
@ -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:
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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}
|
||||
|
||||
<footer class="w-full border border-x-0 border-gray">
|
||||
<a href="/profile">
|
||||
<section class="flex">
|
||||
<img width="40" height="40" src="/images/bored-ape.png" alt="profile" />
|
||||
<div class="p-2 text-gray">@user_name</div>
|
||||
</section>
|
||||
</a>
|
||||
<ProfileNavButton />
|
||||
</footer>
|
||||
</ul>
|
||||
|
||||
|
|
37
modules/gui/src/components/NavBar/ProfileNavButton.svelte
Normal file
37
modules/gui/src/components/NavBar/ProfileNavButton.svelte
Normal file
|
@ -0,0 +1,37 @@
|
|||
<script lang="ts">
|
||||
import { open } from '@tauri-apps/api/shell';
|
||||
import { authStore } from '$libs/stores';
|
||||
import type { Developer } from '@tea/ui/types';
|
||||
import { apiBaseUrl } from '@api';
|
||||
|
||||
let user: Developer | null = null;
|
||||
const deviceId = authStore.deviceIdStore;
|
||||
|
||||
const openGithub = () => {
|
||||
open(`${apiBaseUrl}/auth/user?device_id=${$deviceId}`);
|
||||
try {
|
||||
authStore.pollSession();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
authStore.subscribe((u) => (user = u));
|
||||
</script>
|
||||
|
||||
{#if user}
|
||||
<a href="/profile">
|
||||
<section class="flex">
|
||||
<img width="40" height="40" src={user.avatar_url || '/images/bored-ape.png'} alt="profile" />
|
||||
<div class="p-2 text-gray">@{user.login}</div>
|
||||
</section>
|
||||
</a>
|
||||
{:else}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<section class="flex" on:click={openGithub}>
|
||||
<figure class="p-2">
|
||||
<img width="28" height="28" src="/images/github.png" alt="profile" />
|
||||
</figure>
|
||||
<div class="p-2 text-gray">Login</div>
|
||||
</section>
|
||||
{/if}
|
|
@ -1,23 +1,36 @@
|
|||
<script lang="ts">
|
||||
import '$appcss';
|
||||
import { authStore } from '$libs/stores';
|
||||
import type { Developer } from '@tea/ui/types';
|
||||
|
||||
let user: Developer | null = null;
|
||||
const authPage = `http://localhost:3000/v1/auth/user?device_id=${authStore.deviceId}`; // https://api.tea.xyz/v1/auth/user?device_id=device_id
|
||||
|
||||
authStore.subscribe((u) => (user = u));
|
||||
</script>
|
||||
|
||||
{#if user}
|
||||
<section class="border-2 border-gray bg-black p-2">
|
||||
<div class="profile_banner container flex border border-gray bg-black">
|
||||
<img class="w-1/5" src="/images/bored-ape.png" alt="profile" />
|
||||
<img class="w-1/5" src={user.avatar_url || '/images/bored-ape.png'} alt="profile" />
|
||||
<div class="flex w-4/5 items-center p-5">
|
||||
<div class="w-1/2 pl-5">
|
||||
<p class="uppercase text-gray">Authenticated with GitHub</p>
|
||||
<p />
|
||||
<p class="text-4xl text-primary">@Username</p>
|
||||
<p class="text-4xl text-primary">@{user.login}</p>
|
||||
</div>
|
||||
<div class="h-full border-l border-gray" />
|
||||
<div class="w-1/2 pl-10">
|
||||
<p class="uppercase leading-loose text-gray">
|
||||
Country: <span>Germany</span><br />Wallet:
|
||||
Country: <span>{user?.country}</span><br />Wallet:
|
||||
{#if user.wallet}
|
||||
<span>{user.wallet}</span>
|
||||
{:else}
|
||||
<a class="text-green underline" href="/">Connect Now</a>
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
|
|
@ -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<Category[]> {
|
|||
];
|
||||
}
|
||||
|
||||
export async function getDeviceAuth(deviceId: string): Promise<any> {
|
||||
// const data = await get<any>(`/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<Bottle[]> {
|
||||
return [
|
||||
{ name, platform: 'darwin', arch: 'aarch64', version: '3.39.4' },
|
||||
|
@ -335,3 +355,7 @@ export async function getPackageBottles(name: string): Promise<Bottle[]> {
|
|||
{ name, platform: 'linux', arch: 'x86-64', version: '3.40.0' }
|
||||
];
|
||||
}
|
||||
|
||||
export async function registerDevice(): Promise<string> {
|
||||
return 'uuid1234';
|
||||
}
|
||||
|
|
|
@ -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<T>(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<T>(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<Category[]> {
|
|||
return categories;
|
||||
}
|
||||
|
||||
type DeviceAuth = {
|
||||
status: AuthStatus;
|
||||
user: Developer;
|
||||
key: string;
|
||||
};
|
||||
|
||||
export async function getDeviceAuth(deviceId: string): Promise<DeviceAuth> {
|
||||
const data = await get<DeviceAuth>(`/auth/device/${deviceId}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function getPackageBottles(packageName: string): Promise<Bottle[]> {
|
||||
console.log('getting bottles for ', packageName);
|
||||
const client = await getClient();
|
||||
|
@ -172,3 +188,8 @@ export async function getPackageBottles(packageName: string): Promise<Bottle[]>
|
|||
console.log('got bottles', data);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function registerDevice(): Promise<string> {
|
||||
const { deviceId } = await get<{ deviceId: string }>('/auth/registerDevice');
|
||||
return deviceId;
|
||||
}
|
||||
|
|
|
@ -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<string>('/');
|
||||
|
||||
|
@ -169,3 +170,5 @@ function initSearchStore() {
|
|||
}
|
||||
|
||||
export const searchStore = initSearchStore();
|
||||
|
||||
export const authStore = initAuthStore();
|
||||
|
|
115
modules/gui/src/libs/stores/auth.ts
Normal file
115
modules/gui/src/libs/stores/auth.ts
Normal file
|
@ -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<Session>({});
|
||||
let pollLoop = 0;
|
||||
|
||||
const deviceIdStore = writable<string>('');
|
||||
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<Session | void> => {
|
||||
await createDir(basePath, {
|
||||
dir: BaseDirectory.Home,
|
||||
recursive: true
|
||||
});
|
||||
const session = await getLocalSessionData();
|
||||
return session;
|
||||
};
|
||||
|
||||
const getLocalSessionData = async (): Promise<Session | void> => {
|
||||
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
|
||||
});
|
||||
};
|
|
@ -29,3 +29,10 @@ export type Category = {
|
|||
cta_label: string;
|
||||
packages: GUIPackage[];
|
||||
};
|
||||
|
||||
export enum AuthStatus {
|
||||
UNKNOWN = 'UNKNOWN',
|
||||
PENDING = 'PENDING',
|
||||
SUCCESS = 'SUCCESS',
|
||||
FAILED = 'FAILED'
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
<div>
|
||||
<PageHeader>PROFILE</PageHeader>
|
||||
|
||||
<section>
|
||||
<ProfileBanner />
|
||||
</section>
|
||||
|
|
BIN
modules/gui/static/images/github.png
Normal file
BIN
modules/gui/static/images/github.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue