mirror of
https://github.com/ivabus/gui
synced 2025-06-07 15:50:27 +03:00
#186 convert auth into electron setup
This commit is contained in:
parent
680e10700d
commit
957cba2a67
12 changed files with 159 additions and 153 deletions
|
@ -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);
|
||||
});
|
||||
|
|
41
modules/desktop/electron/libs/auth.ts
Normal file
41
modules/desktop/electron/libs/auth.ts
Normal file
|
@ -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<Session> {
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
17
modules/desktop/electron/libs/v1Client.ts
Normal file
17
modules/desktop/electron/libs/v1Client.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import axios from 'axios';
|
||||
import path from 'path';
|
||||
|
||||
const base = 'https://api.tea.xyz';
|
||||
export async function get<T>(urlPath: string) {
|
||||
const url = new URL(path.join('v1', urlPath), base).toString();
|
||||
// TODO: add headers
|
||||
const req = await axios.request<T>({
|
||||
method: 'GET',
|
||||
url,
|
||||
headers: {}
|
||||
});
|
||||
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export default get;
|
|
@ -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": [
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
<script lang="ts">
|
||||
// import { authStore } from '$libs/stores';
|
||||
import { authStore } from '$libs/stores';
|
||||
import type { Developer } from '@tea/ui/types';
|
||||
// import { baseUrl } from '$libs/v1Client';
|
||||
import { baseUrl } from '$libs/v1Client';
|
||||
|
||||
const { shell } = window.require('electron');
|
||||
|
||||
let user: Developer | null = null;
|
||||
// const deviceId = authStore.deviceIdStore;
|
||||
const deviceId = authStore.deviceIdStore;
|
||||
|
||||
const openGithub = () => {
|
||||
// open(`${baseUrl}/auth/user?device_id=${$deviceId}`);
|
||||
shell.openExternal(`${baseUrl}/auth/user?device_id=${$deviceId}`)
|
||||
try {
|
||||
// authStore.pollSession();
|
||||
authStore.pollSession();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
// authStore.subscribe((u) => (user = u));
|
||||
authStore.subscribe((u) => (user = u));
|
||||
</script>
|
||||
|
||||
{#if user}
|
||||
|
|
|
@ -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<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'
|
||||
};
|
||||
const data = await v1Client.get<any>(`/auth/device/${deviceId}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function getPackageBottles(name: string): Promise<Bottle[]> {
|
||||
|
|
|
@ -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<Session | null> => {
|
||||
// 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<string>('');
|
||||
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<Session | void> => {
|
||||
// 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<Session | void> => {
|
||||
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');
|
||||
};
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -42,3 +42,8 @@ export type DeviceAuth = {
|
|||
user: Developer;
|
||||
key: string;
|
||||
};
|
||||
export interface Session {
|
||||
device_id?: string;
|
||||
key?: string;
|
||||
user?: Developer;
|
||||
}
|
||||
|
|
|
@ -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<T>(path: string, query?: { [key: string]: string }) {
|
||||
console.log(`GET /api/${path}`);
|
||||
|
||||
await app.isReady(); // wait for electrong dont remove
|
||||
export async function get<T>(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) {
|
||||
|
|
|
@ -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==}
|
||||
|
|
Loading…
Reference in a new issue