diff --git a/modules/desktop/electron/electron.ts b/modules/desktop/electron/electron.ts index 40c25a3b..3538e772 100644 --- a/modules/desktop/electron/electron.ts +++ b/modules/desktop/electron/electron.ts @@ -6,14 +6,17 @@ import path from 'path'; import fs from 'fs'; import { getInstalledPackages } from './libs/teaDir'; -try { - //@ts-ignore only used in dev should not be packaged inprod - /* eslint-disable */ - const er = require('electron-reloader'); - er(module); -} catch (e) { - console.error(e); -} +import { readSessionData, writeSessionData } from './libs/auth'; +import type { Session } from '../src/libs/types'; + +// try { +// //@ts-ignore only used in dev should not be packaged inprod +// /* eslint-disable */ +// const er = require('electron-reloader'); +// er(module); +// } catch (e) { +// console.error(e); +// } const serveURL = serve({ directory: '.' }); const port = process.env.PORT || 3000; @@ -113,3 +116,14 @@ ipcMain.handle('get-installed-packages', async () => { const pkgs = await getInstalledPackages(); return pkgs; }); + +ipcMain.handle('get-session', async () => { + console.log('get session'); + const session = await readSessionData(); + console.log('session:', session); + return session; +}); + +ipcMain.handle('update-session', async (_, data) => { + await writeSessionData(data as Session); +}); diff --git a/modules/desktop/electron/libs/auth.ts b/modules/desktop/electron/libs/auth.ts new file mode 100644 index 00000000..55a9fed9 --- /dev/null +++ b/modules/desktop/electron/libs/auth.ts @@ -0,0 +1,41 @@ +import { mkdirp } from 'mkdirp'; +import path from 'path'; +import fs from 'fs'; +import { getTeaPath } from './teaDir'; +import type { Session } from '../../src/libs/types'; +import * as v1Client from './v1Client'; + +const sessionFilePath = path.join(getTeaPath(), 'tea.xyz/gui/tmp.dat'); +const sessionFolder = path.join(getTeaPath(), 'tea.xyz/gui'); + +export async function initSessionData() { + fs.readFileSync(sessionFilePath); + + await mkdirp(sessionFolder); + const req = await v1Client.get<{ deviceId: string }>('/auth/registerDevice'); +} + +export async function readSessionData(): Promise { + try { + const sessionBuffer = await fs.readFileSync(sessionFilePath); + const session = JSON.parse(sessionBuffer.toString()) as Session; + return session; + } catch (error) { + console.error(error); + const req = await v1Client.get<{ deviceId: string }>('/auth/registerDevice'); + const data = { device_id: req.deviceId }; + await writeSessionData(data); + return data; + } +} + +export async function writeSessionData(data: Session) { + try { + await mkdirp(sessionFolder); + await fs.writeFileSync(sessionFilePath, JSON.stringify(data), { + encoding: 'utf-8' + }); + } catch (error) { + console.error(error); + } +} diff --git a/modules/desktop/electron/libs/teaDir.ts b/modules/desktop/electron/libs/teaDir.ts index 0cbe9d3e..85889ffa 100644 --- a/modules/desktop/electron/libs/teaDir.ts +++ b/modules/desktop/electron/libs/teaDir.ts @@ -9,9 +9,15 @@ type Dir = { path: string; children?: Dir[]; }; -export async function getInstalledPackages() { + +export const getTeaPath = () => { const homePath = app.getPath('home'); - const pkgsPath = path.join(homePath, './.tea'); + const teaPath = path.join(homePath, './.tea'); + return teaPath; +}; + +export async function getInstalledPackages() { + const pkgsPath = getTeaPath(); const folders = await deepReadDir({ dir: pkgsPath, diff --git a/modules/desktop/electron/libs/v1Client.ts b/modules/desktop/electron/libs/v1Client.ts new file mode 100644 index 00000000..1a54acc0 --- /dev/null +++ b/modules/desktop/electron/libs/v1Client.ts @@ -0,0 +1,17 @@ +import axios from 'axios'; +import path from 'path'; + +const base = 'https://api.tea.xyz'; +export async function get(urlPath: string) { + const url = new URL(path.join('v1', urlPath), base).toString(); + // TODO: add headers + const req = await axios.request({ + method: 'GET', + url, + headers: {} + }); + + return req.data; +} + +export default get; diff --git a/modules/desktop/package.json b/modules/desktop/package.json index 790e0a4f..04307b44 100644 --- a/modules/desktop/package.json +++ b/modules/desktop/package.json @@ -70,8 +70,8 @@ "dependencies": { "@electron/asar": "^3.2.3", "@types/bcryptjs": "^2.4.2", - "@types/url-join": "^4.0.1", "@vitest/coverage-c8": "^0.27.1", + "axios": "^1.3.2", "bcryptjs": "^2.4.3", "buffer": "^6.0.3", "electron-context-menu": "^3.6.1", @@ -82,11 +82,11 @@ "fuse.js": "^6.6.2", "lodash": "^4.17.21", "lorem-ipsum": "^2.0.8", + "mkdirp": "^2.1.3", "semver": "^7.3.8", "svelte-markdown": "^0.2.3", "svelte-watch-resize": "^1.0.3", - "upath": "^2.0.1", - "url-join": "^5.0.0" + "upath": "^2.0.1" }, "pnpm": { "onlyBuiltDependencies": [ diff --git a/modules/desktop/src/components/TopBar/ProfileNavButton.svelte b/modules/desktop/src/components/TopBar/ProfileNavButton.svelte index f0d2bb06..96985d9b 100644 --- a/modules/desktop/src/components/TopBar/ProfileNavButton.svelte +++ b/modules/desktop/src/components/TopBar/ProfileNavButton.svelte @@ -1,21 +1,23 @@ {#if user} diff --git a/modules/desktop/src/libs/api/mock.ts b/modules/desktop/src/libs/api/mock.ts index bfc12325..fea8128f 100644 --- a/modules/desktop/src/libs/api/mock.ts +++ b/modules/desktop/src/libs/api/mock.ts @@ -10,7 +10,9 @@ import type { GUIPackage, Course, Category } from '../types'; import { PackageStates } from '../types'; import { loremIpsum } from 'lorem-ipsum'; import _ from 'lodash'; -import { getInstalledPackages } from '$libs/teaDir'; +// import { getInstalledPackages } from '$libs/teaDir'; +// import { getSession } from '$libs/stores/auth'; +import * as v1Client from '$libs/v1Client'; const packages: Package[] = [ { @@ -324,21 +326,8 @@ 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' - }; + const data = await v1Client.get(`/auth/device/${deviceId}`); + return data; } export async function getPackageBottles(name: string): Promise { diff --git a/modules/desktop/src/libs/stores/auth.ts b/modules/desktop/src/libs/stores/auth.ts index 289f8d89..434d899a 100644 --- a/modules/desktop/src/libs/stores/auth.ts +++ b/modules/desktop/src/libs/stores/auth.ts @@ -4,28 +4,16 @@ import { writable } from 'svelte/store'; // import fs from 'fs'; import { getDeviceAuth, registerDevice } from '@api'; import type { Developer } from '@tea/ui/types'; +import type { Session } from '$libs/types'; + +const { ipcRenderer } = window.require('electron'); const basePath = '.tea/tea.xyz/gui'; -export interface Session { - device_id?: string; - key?: string; - user?: Developer; -} export let session: Session | null = null; export const getSession = async (): Promise => { - // await app.whenReady(); - // if (session && session?.user) return session; - // const homePath = app.getPath('home'); - // const sessionFilePath = await join(homePath, basePath, 'tmp.dat'); - // try { - // const encryptedData = fs.readFileSync(sessionFilePath, 'utf-8'); - // session = JSON.parse(encryptedData || '{}') as Session; - // return session; - // } catch (error) { - // return null; - // } - return null; + session = await ipcRenderer.invoke('get-session'); + return session; }; export default function initAuthStore() { @@ -35,7 +23,7 @@ export default function initAuthStore() { const deviceIdStore = writable(''); let deviceId = ''; - initSession().then((sess) => { + getSession().then((sess) => { if (sess) { session = sess; sessionStore.set(sess); @@ -52,7 +40,8 @@ export default function initAuthStore() { key: data.key, user: data.user }; - saveLocallySessionData(localSession); + console.log('localSession:', localSession); + await ipcRenderer.invoke('update-session', localSession); sessionStore.set(localSession); } @@ -93,39 +82,3 @@ export default function initAuthStore() { pollSession }; } - -const initSession = async (): Promise => { - // const homePath = app.getPath('home'); - // try { - // await fs.mkdirSync(join(homePath, basePath)); - // } catch (error) { - // console.error(error); - // } - // const session = await getLocalSessionData(); - // return session; -}; - -const getLocalSessionData = async (): Promise => { - let data: Session; - try { - const session = await getSession(); - if (!session) throw new Error('no session'); - data = session; - } catch (error) { - console.error('register device:', error); - const deviceId = await registerDevice(); - data = { - device_id: deviceId - }; - await saveLocallySessionData(data); - } - - return data; -}; - -const saveLocallySessionData = async (data: Session) => { - // const homePath = app.getPath('home'); - // const sessionFilePath = await join(homePath, basePath, 'tmp.dat'); - // // TODO: encrypt first - // await fs.writeFileSync(sessionFilePath, JSON.stringify(data), 'utf-8'); -}; diff --git a/modules/desktop/src/libs/teaDir.ts b/modules/desktop/src/libs/teaDir.ts index a4fc318f..18fe981f 100644 --- a/modules/desktop/src/libs/teaDir.ts +++ b/modules/desktop/src/libs/teaDir.ts @@ -10,36 +10,8 @@ type Dir = { const { ipcRenderer } = window.require('electron'); export async function getInstalledPackages() { - console.log('get installed pkgs'); const pkgs = await ipcRenderer.invoke('get-installed-packages'); - console.log(pkgs); - // const homePath = app.getPath('home'); - // const packageFolders = (await readDir('.tea/', { - // dir: BaseDirectory.Home, - // recursive: true - // })) as Dir[]; - - // const pkgs = packageFolders - // .filter((p) => p.name !== 'tea.xyz') - // .map(getPkgBottles) - // .filter((pkgBottles) => pkgBottles.length) - // .map((pkgBottles) => { - // const versions = pkgBottles.map((v) => v.split('/v')[1]); - // const full_name = pkgBottles[0].split('/v')[0]; - - // const isSemverVersion = versions.filter((v) => semverTest.test(v)); - // const isNotAsterisk = versions.filter((v) => v !== '*'); - // const version = - // (isSemverVersion.length && isSemverVersion[0]) || - // (isNotAsterisk.length && isNotAsterisk[0]) || - // '*'; - // return { - // version, - // full_name - // }; - // }); - // return pkgs; - return []; + return pkgs as { version: string; full_name: string }; } const semverTest = diff --git a/modules/desktop/src/libs/types.ts b/modules/desktop/src/libs/types.ts index 56367999..7c956e14 100644 --- a/modules/desktop/src/libs/types.ts +++ b/modules/desktop/src/libs/types.ts @@ -42,3 +42,8 @@ export type DeviceAuth = { user: Developer; key: string; }; +export interface Session { + device_id?: string; + key?: string; + user?: Developer; +} diff --git a/modules/desktop/src/libs/v1Client.ts b/modules/desktop/src/libs/v1Client.ts index 99beb772..670e28df 100644 --- a/modules/desktop/src/libs/v1Client.ts +++ b/modules/desktop/src/libs/v1Client.ts @@ -1,49 +1,27 @@ -import { net, app } from 'electron'; -import type { Session } from '$libs/stores/auth'; +import axios from 'axios'; +import type { Session } from '$libs/types'; import bcrypt from 'bcryptjs'; import { getSession } from '$libs/stores/auth'; -import urlJoin from 'url-join'; export const baseUrl = 'https://api.tea.xyz/v1'; -export async function get(path: string, query?: { [key: string]: string }) { - console.log(`GET /api/${path}`); - - await app.isReady(); // wait for electrong dont remove +export async function get(urlPath: string, query?: { [key: string]: string }) { + console.log(`GET /v1/${urlPath}`); const [session] = await Promise.all([getSession()]); const headers = session?.device_id && session?.user - ? await getHeaders(`GET/${path}`, session) + ? await getHeaders(`GET/${urlPath}`, session) : { Authorization: 'public ' }; - return new Promise((resolve, reject) => { - const url = urlJoin(baseUrl, path); - - const req = net.request({ - method: 'GET', - url - }); - - for (const k in headers) { - const v = headers[k as keyof typeof headers]; - if (v) req.setHeader(k, v); - } - - const buffer: Buffer[] = []; - req.on('response', (res) => { - res.on('error', reject); - res.on('data', (b) => buffer.push(b)); - res.on('end', () => { - const bodyRaw = Buffer.concat(buffer); - const body = JSON.parse(bodyRaw.toString()); - resolve(body); - }); - }); - - req.on('error', reject); + const req = await axios.request({ + method: 'GET', + baseURL: 'https://api.tea.xyz', + url: ['v1', ...urlPath.split('/')].filter((p) => p).join('/') }); + + return req.data as T; } async function getHeaders(path: string, session: Session) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3c5e2b20..c22c5f2c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,6 +25,7 @@ importers: '@typescript-eslint/parser': ^5.27.0 '@vitest/coverage-c8': ^0.27.1 autoprefixer: ^10.4.13 + axios: ^1.3.2 bcryptjs: ^2.4.3 buffer: ^6.0.3 concurrently: ^7.6.0 @@ -44,6 +45,7 @@ importers: jsdom: ^21.0.0 lodash: ^4.17.21 lorem-ipsum: ^2.0.8 + mkdirp: ^2.1.3 postcss: ^8.4.19 prettier: ^2.7.1 prettier-plugin-svelte: ^2.7.0 @@ -67,6 +69,7 @@ importers: '@types/bcryptjs': 2.4.2 '@types/url-join': 4.0.1 '@vitest/coverage-c8': 0.27.1_jsdom@21.0.0 + axios: 1.3.2 bcryptjs: 2.4.3 buffer: 6.0.3 electron-context-menu: 3.6.1 @@ -77,6 +80,7 @@ importers: fuse.js: 6.6.2 lodash: 4.17.21 lorem-ipsum: 2.0.8 + mkdirp: 2.1.3 semver: 7.3.8 svelte-markdown: 0.2.3_svelte@3.55.1 svelte-watch-resize: 1.0.3 @@ -5373,6 +5377,16 @@ packages: engines: {node: '>= 0.4'} dev: true + /axios/1.3.2: + resolution: {integrity: sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==} + dependencies: + follow-redirects: 1.15.2 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false + /babel-core/7.0.0-bridge.0_@babel+core@7.20.2: resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} peerDependencies: @@ -7995,6 +8009,16 @@ packages: tslib: 1.14.1 dev: true + /follow-redirects/1.15.2: + resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + /for-each/0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: @@ -10167,6 +10191,12 @@ packages: hasBin: true dev: true + /mkdirp/2.1.3: + resolution: {integrity: sha512-sjAkg21peAG9HS+Dkx7hlG9Ztx7HLeKnvB3NQRcu/mltCVmvkF0pisbiTSfDVYTT86XEfZrTUosLdZLStquZUw==} + engines: {node: '>=10'} + hasBin: true + dev: false + /mlly/1.1.0: resolution: {integrity: sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==} dependencies: @@ -10890,7 +10920,6 @@ packages: /proxy-from-env/1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: true /psl/1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}