#186 convert auth into electron setup

This commit is contained in:
neil 2023-02-07 15:43:16 +08:00
parent 680e10700d
commit 957cba2a67
12 changed files with 159 additions and 153 deletions

View file

@ -6,14 +6,17 @@ import path from 'path';
import fs from 'fs'; import fs from 'fs';
import { getInstalledPackages } from './libs/teaDir'; import { getInstalledPackages } from './libs/teaDir';
try { import { readSessionData, writeSessionData } from './libs/auth';
//@ts-ignore only used in dev should not be packaged inprod import type { Session } from '../src/libs/types';
/* eslint-disable */
const er = require('electron-reloader'); // try {
er(module); // //@ts-ignore only used in dev should not be packaged inprod
} catch (e) { // /* eslint-disable */
console.error(e); // const er = require('electron-reloader');
} // er(module);
// } catch (e) {
// console.error(e);
// }
const serveURL = serve({ directory: '.' }); const serveURL = serve({ directory: '.' });
const port = process.env.PORT || 3000; const port = process.env.PORT || 3000;
@ -113,3 +116,14 @@ ipcMain.handle('get-installed-packages', async () => {
const pkgs = await getInstalledPackages(); const pkgs = await getInstalledPackages();
return pkgs; 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);
});

View 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);
}
}

View file

@ -9,9 +9,15 @@ type Dir = {
path: string; path: string;
children?: Dir[]; children?: Dir[];
}; };
export async function getInstalledPackages() {
export const getTeaPath = () => {
const homePath = app.getPath('home'); 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({ const folders = await deepReadDir({
dir: pkgsPath, dir: pkgsPath,

View 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;

View file

@ -70,8 +70,8 @@
"dependencies": { "dependencies": {
"@electron/asar": "^3.2.3", "@electron/asar": "^3.2.3",
"@types/bcryptjs": "^2.4.2", "@types/bcryptjs": "^2.4.2",
"@types/url-join": "^4.0.1",
"@vitest/coverage-c8": "^0.27.1", "@vitest/coverage-c8": "^0.27.1",
"axios": "^1.3.2",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"electron-context-menu": "^3.6.1", "electron-context-menu": "^3.6.1",
@ -82,11 +82,11 @@
"fuse.js": "^6.6.2", "fuse.js": "^6.6.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lorem-ipsum": "^2.0.8", "lorem-ipsum": "^2.0.8",
"mkdirp": "^2.1.3",
"semver": "^7.3.8", "semver": "^7.3.8",
"svelte-markdown": "^0.2.3", "svelte-markdown": "^0.2.3",
"svelte-watch-resize": "^1.0.3", "svelte-watch-resize": "^1.0.3",
"upath": "^2.0.1", "upath": "^2.0.1"
"url-join": "^5.0.0"
}, },
"pnpm": { "pnpm": {
"onlyBuiltDependencies": [ "onlyBuiltDependencies": [

View file

@ -1,21 +1,23 @@
<script lang="ts"> <script lang="ts">
// import { authStore } from '$libs/stores'; import { authStore } from '$libs/stores';
import type { Developer } from '@tea/ui/types'; 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; let user: Developer | null = null;
// const deviceId = authStore.deviceIdStore; const deviceId = authStore.deviceIdStore;
const openGithub = () => { const openGithub = () => {
// open(`${baseUrl}/auth/user?device_id=${$deviceId}`); shell.openExternal(`${baseUrl}/auth/user?device_id=${$deviceId}`)
try { try {
// authStore.pollSession(); authStore.pollSession();
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
}; };
// authStore.subscribe((u) => (user = u)); authStore.subscribe((u) => (user = u));
</script> </script>
{#if user} {#if user}

View file

@ -10,7 +10,9 @@ import type { GUIPackage, Course, Category } from '../types';
import { PackageStates } from '../types'; import { PackageStates } from '../types';
import { loremIpsum } from 'lorem-ipsum'; import { loremIpsum } from 'lorem-ipsum';
import _ from 'lodash'; 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[] = [ const packages: Package[] = [
{ {
@ -324,21 +326,8 @@ export async function getCategorizedPackages(): Promise<Category[]> {
} }
export async function getDeviceAuth(deviceId: string): Promise<any> { export async function getDeviceAuth(deviceId: string): Promise<any> {
// const data = await get<any>(`/auth/device/${deviceId}`); const data = await v1Client.get<any>(`/auth/device/${deviceId}`);
return { return data;
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[]> { export async function getPackageBottles(name: string): Promise<Bottle[]> {

View file

@ -4,28 +4,16 @@ import { writable } from 'svelte/store';
// import fs from 'fs'; // import fs from 'fs';
import { getDeviceAuth, registerDevice } from '@api'; import { getDeviceAuth, registerDevice } from '@api';
import type { Developer } from '@tea/ui/types'; import type { Developer } from '@tea/ui/types';
import type { Session } from '$libs/types';
const { ipcRenderer } = window.require('electron');
const basePath = '.tea/tea.xyz/gui'; const basePath = '.tea/tea.xyz/gui';
export interface Session {
device_id?: string;
key?: string;
user?: Developer;
}
export let session: Session | null = null; export let session: Session | null = null;
export const getSession = async (): Promise<Session | null> => { export const getSession = async (): Promise<Session | null> => {
// await app.whenReady(); session = await ipcRenderer.invoke('get-session');
// if (session && session?.user) return session; 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;
}; };
export default function initAuthStore() { export default function initAuthStore() {
@ -35,7 +23,7 @@ export default function initAuthStore() {
const deviceIdStore = writable<string>(''); const deviceIdStore = writable<string>('');
let deviceId = ''; let deviceId = '';
initSession().then((sess) => { getSession().then((sess) => {
if (sess) { if (sess) {
session = sess; session = sess;
sessionStore.set(sess); sessionStore.set(sess);
@ -52,7 +40,8 @@ export default function initAuthStore() {
key: data.key, key: data.key,
user: data.user user: data.user
}; };
saveLocallySessionData(localSession); console.log('localSession:', localSession);
await ipcRenderer.invoke('update-session', localSession);
sessionStore.set(localSession); sessionStore.set(localSession);
} }
@ -93,39 +82,3 @@ export default function initAuthStore() {
pollSession 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');
};

View file

@ -10,36 +10,8 @@ type Dir = {
const { ipcRenderer } = window.require('electron'); const { ipcRenderer } = window.require('electron');
export async function getInstalledPackages() { export async function getInstalledPackages() {
console.log('get installed pkgs');
const pkgs = await ipcRenderer.invoke('get-installed-packages'); const pkgs = await ipcRenderer.invoke('get-installed-packages');
console.log(pkgs); return pkgs as { version: string; full_name: string };
// 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 [];
} }
const semverTest = const semverTest =

View file

@ -42,3 +42,8 @@ export type DeviceAuth = {
user: Developer; user: Developer;
key: string; key: string;
}; };
export interface Session {
device_id?: string;
key?: string;
user?: Developer;
}

View file

@ -1,49 +1,27 @@
import { net, app } from 'electron'; import axios from 'axios';
import type { Session } from '$libs/stores/auth'; import type { Session } from '$libs/types';
import bcrypt from 'bcryptjs'; import bcrypt from 'bcryptjs';
import { getSession } from '$libs/stores/auth'; import { getSession } from '$libs/stores/auth';
import urlJoin from 'url-join';
export const baseUrl = 'https://api.tea.xyz/v1'; export const baseUrl = 'https://api.tea.xyz/v1';
export async function get<T>(path: string, query?: { [key: string]: string }) { export async function get<T>(urlPath: string, query?: { [key: string]: string }) {
console.log(`GET /api/${path}`); console.log(`GET /v1/${urlPath}`);
await app.isReady(); // wait for electrong dont remove
const [session] = await Promise.all([getSession()]); const [session] = await Promise.all([getSession()]);
const headers = const headers =
session?.device_id && session?.user session?.device_id && session?.user
? await getHeaders(`GET/${path}`, session) ? await getHeaders(`GET/${urlPath}`, session)
: { Authorization: 'public ' }; : { Authorization: 'public ' };
return new Promise((resolve, reject) => { const req = await axios.request({
const url = urlJoin(baseUrl, path); method: 'GET',
baseURL: 'https://api.tea.xyz',
const req = net.request({ url: ['v1', ...urlPath.split('/')].filter((p) => p).join('/')
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);
}); });
return req.data as T;
} }
async function getHeaders(path: string, session: Session) { async function getHeaders(path: string, session: Session) {

View file

@ -25,6 +25,7 @@ importers:
'@typescript-eslint/parser': ^5.27.0 '@typescript-eslint/parser': ^5.27.0
'@vitest/coverage-c8': ^0.27.1 '@vitest/coverage-c8': ^0.27.1
autoprefixer: ^10.4.13 autoprefixer: ^10.4.13
axios: ^1.3.2
bcryptjs: ^2.4.3 bcryptjs: ^2.4.3
buffer: ^6.0.3 buffer: ^6.0.3
concurrently: ^7.6.0 concurrently: ^7.6.0
@ -44,6 +45,7 @@ importers:
jsdom: ^21.0.0 jsdom: ^21.0.0
lodash: ^4.17.21 lodash: ^4.17.21
lorem-ipsum: ^2.0.8 lorem-ipsum: ^2.0.8
mkdirp: ^2.1.3
postcss: ^8.4.19 postcss: ^8.4.19
prettier: ^2.7.1 prettier: ^2.7.1
prettier-plugin-svelte: ^2.7.0 prettier-plugin-svelte: ^2.7.0
@ -67,6 +69,7 @@ importers:
'@types/bcryptjs': 2.4.2 '@types/bcryptjs': 2.4.2
'@types/url-join': 4.0.1 '@types/url-join': 4.0.1
'@vitest/coverage-c8': 0.27.1_jsdom@21.0.0 '@vitest/coverage-c8': 0.27.1_jsdom@21.0.0
axios: 1.3.2
bcryptjs: 2.4.3 bcryptjs: 2.4.3
buffer: 6.0.3 buffer: 6.0.3
electron-context-menu: 3.6.1 electron-context-menu: 3.6.1
@ -77,6 +80,7 @@ importers:
fuse.js: 6.6.2 fuse.js: 6.6.2
lodash: 4.17.21 lodash: 4.17.21
lorem-ipsum: 2.0.8 lorem-ipsum: 2.0.8
mkdirp: 2.1.3
semver: 7.3.8 semver: 7.3.8
svelte-markdown: 0.2.3_svelte@3.55.1 svelte-markdown: 0.2.3_svelte@3.55.1
svelte-watch-resize: 1.0.3 svelte-watch-resize: 1.0.3
@ -5373,6 +5377,16 @@ packages:
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dev: true 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: /babel-core/7.0.0-bridge.0_@babel+core@7.20.2:
resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==}
peerDependencies: peerDependencies:
@ -7995,6 +8009,16 @@ packages:
tslib: 1.14.1 tslib: 1.14.1
dev: true 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: /for-each/0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
dependencies: dependencies:
@ -10167,6 +10191,12 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/mkdirp/2.1.3:
resolution: {integrity: sha512-sjAkg21peAG9HS+Dkx7hlG9Ztx7HLeKnvB3NQRcu/mltCVmvkF0pisbiTSfDVYTT86XEfZrTUosLdZLStquZUw==}
engines: {node: '>=10'}
hasBin: true
dev: false
/mlly/1.1.0: /mlly/1.1.0:
resolution: {integrity: sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==} resolution: {integrity: sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==}
dependencies: dependencies:
@ -10890,7 +10920,6 @@ packages:
/proxy-from-env/1.1.0: /proxy-from-env/1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
dev: true
/psl/1.9.0: /psl/1.9.0:
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}