Skip to content

Commit

Permalink
feat: get current user
Browse files Browse the repository at this point in the history
  • Loading branch information
Akryum committed Jan 2, 2024
1 parent b106325 commit 66f4378
Show file tree
Hide file tree
Showing 16 changed files with 328 additions and 4 deletions.
4 changes: 4 additions & 0 deletions packages/app/components/AppNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,9 @@ const colorMode = useColorMode()
title="Config"
shortcuts="meta_,"
/>

<UserCurrent
class="mt-2"
/>
</nav>
</template>
62 changes: 62 additions & 0 deletions packages/app/components/user/UserAvatar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<script lang="ts" setup>
import { Menu } from 'floating-vue'
import type { User } from '@moquerie/core'
defineProps<{
user: User
}>()
</script>

<template>
<Menu
placement="right"
:delay="500"
:dispose-timeout="0"
>
<UAvatar
:src="user.avatar"
:alt="user.name"
/>

<template #popper>
<div class="p-4 flex items-center gap-4">
<UAvatar
v-if="user.avatar"
:src="user.avatar"
:alt="user.name"
size="2xl"
/>

<div>
<div class="text-md">
{{ user.name }}
</div>
<div
v-if="user.email"
class="text-gray-500"
>
{{ user.email }}
</div>
<div v-if="user.github" class="flex items-center gap-0.5">
<UIcon
name="i-ph-github-logo"
class="w-4 h-4 flex-none"
/>
<a
v-if="user.github.profilePageUrl"
:href="user.github.profilePageUrl"
target="_blank"
rel="noopener noreferrer"
class="text-primary-500 hover:underline"
>
{{ user.github.login }}
</a>
<span v-else>
{{ user.github.login }}
</span>
</div>
</div>
</div>
</template>
</Menu>
</template>
11 changes: 11 additions & 0 deletions packages/app/components/user/UserCurrent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script lang="ts" setup>
const { data, refresh } = useFetch('/api/user/current')
onWindowFocus(refresh)
</script>

<template>
<UserAvatar
v-if="data"
:user="data"
/>
</template>
3 changes: 3 additions & 0 deletions packages/app/server/api/user/current/index.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { getCurrentUser } from '@moquerie/core'

export default defineEventHandler(() => getCurrentUser())
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"express": "^4.18.2",
"fast-glob": "^3.3.2",
"get-port-please": "^3.1.1",
"git-user-info": "^2.0.3",
"graphql-yoga": "^5.0.2",
"jiti": "^1.21.0",
"lru-cache": "^10.1.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/fieldActions/fieldActionWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type FSWatcher, watch } from 'chokidar'
import createJITI, { type JITI } from 'jiti'
import path from 'pathe'
import { getCwd } from '../util/env.js'
import type { FieldAction } from '../types/field-action.js'
import type { FieldAction } from '../types/fieldAction.js'
import type { ResourceSchema } from '../types/resource.js'

export interface FieldActionWatcherConfig {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './storage/index.js'
export type * from './types/index.js'
export * from './context.js'
export * from './resource.js'
export * from './user/index.js'
File renamed without changes.
3 changes: 2 additions & 1 deletion packages/core/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export type * from './config.js'
export type * from './db.js'
export type * from './factory.js'
export type * from './field-action.js'
export type * from './fieldAction.js'
export type * from './resource.js'
export type * from './user.js'
9 changes: 9 additions & 0 deletions packages/core/src/types/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface User {
name: string
email?: string
avatar?: string
github?: {
login: string
profilePageUrl?: string
}
}
19 changes: 19 additions & 0 deletions packages/core/src/user/findOnGithub.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export interface GithubUser {
login: string
avatar?: string
profilePageUrl?: string
}

export async function findUserOnGithub(email: string): Promise<GithubUser | null> {
const r = await fetch(`https://github.com/gitapi/search/users?q=${email}+in:email`)
const json = await r.json()
if (!json.items.length) {
return null
}
const item = json.items[0]
return {
login: item.login,
avatar: item.avatar_url,
profilePageUrl: item.html_url,
}
}
51 changes: 51 additions & 0 deletions packages/core/src/user/getCurrentUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import os from 'node:os'
import { LRUCache } from 'lru-cache'
import { getGitUserInfo } from 'git-user-info'
import type { User } from '../types/user.js'
import { findUserOnGithub } from './findOnGithub.js'

const userCache = new LRUCache<string, User>({
max: 20,
ttl: 1000 * 60 * 60, // 1 hour
})

export async function getCurrentUser() {
const cachedUser = userCache.get('current')
if (cachedUser) {
return cachedUser
}

const user: User = {
name: os.userInfo().username,
}

try {
const gitUser = await getGitUserInfo()
if (gitUser) {
user.name = gitUser.name
user.email = gitUser.email
}
}
catch (e) {
// ignore
}

if (user.email) {
try {
const githubUser = await findUserOnGithub(user.email)
if (githubUser) {
user.avatar = githubUser.avatar
user.github = {
login: githubUser.login,
profilePageUrl: githubUser.profilePageUrl,
}
}
}
catch (e) {
// ignore
}
}

userCache.set('current', user)
return user
}
2 changes: 2 additions & 0 deletions packages/core/src/user/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './findOnGithub.js'
export * from './getCurrentUser.js'
82 changes: 81 additions & 1 deletion playground/graphql.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"queryType": {
"name": "Query"
},
"mutationType": null,
"mutationType": {
"name": "Mutation"
},
"subscriptionType": null,
"types": [
{
Expand All @@ -26,6 +28,16 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "Int",
"description": "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Message",
Expand Down Expand Up @@ -176,6 +188,58 @@
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Mutation",
"description": null,
"fields": [
{
"name": "addHello",
"description": null,
"args": [
{
"name": "message",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Query",
Expand Down Expand Up @@ -228,6 +292,22 @@
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "manyHellosCount",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
Expand Down
22 changes: 22 additions & 0 deletions playground/src/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?:
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
export type RequireFields<T, K extends keyof T> = Omit<T, K> & { [P in K]-?: NonNullable<T[P]> };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: { input: string; output: string; }
Expand Down Expand Up @@ -35,11 +36,22 @@ export type MessageType =
| 'private'
| 'public';

export type Mutation = {
__typename?: 'Mutation';
addHello: Array<Scalars['String']['output']>;
};


export type MutationAddHelloArgs = {
message: Scalars['String']['input'];
};

export type Query = {
__typename?: 'Query';
currentUser?: Maybe<User>;
hello?: Maybe<Scalars['String']['output']>;
manyHellos: Array<Scalars['String']['output']>;
manyHellosCount: Scalars['Int']['output'];
};

export type User = {
Expand Down Expand Up @@ -124,8 +136,10 @@ export type DirectiveResolverFn<TResult = {}, TParent = {}, TContext = {}, TArgs
export type ResolversTypes = {
Boolean: ResolverTypeWrapper<Scalars['Boolean']['output']>;
ID: ResolverTypeWrapper<Scalars['ID']['output']>;
Int: ResolverTypeWrapper<Scalars['Int']['output']>;
Message: ResolverTypeWrapper<Message>;
MessageType: MessageType;
Mutation: ResolverTypeWrapper<{}>;
Query: ResolverTypeWrapper<{}>;
String: ResolverTypeWrapper<Scalars['String']['output']>;
User: ResolverTypeWrapper<User>;
Expand All @@ -135,7 +149,9 @@ export type ResolversTypes = {
export type ResolversParentTypes = {
Boolean: Scalars['Boolean']['output'];
ID: Scalars['ID']['output'];
Int: Scalars['Int']['output'];
Message: Message;
Mutation: {};
Query: {};
String: Scalars['String']['output'];
User: User;
Expand All @@ -152,10 +168,15 @@ export type MessageResolvers<ContextType = any, ParentType extends ResolversPare
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};

export type MutationResolvers<ContextType = any, ParentType extends ResolversParentTypes['Mutation'] = ResolversParentTypes['Mutation']> = {
addHello?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType, RequireFields<MutationAddHelloArgs, 'message'>>;
};

export type QueryResolvers<ContextType = any, ParentType extends ResolversParentTypes['Query'] = ResolversParentTypes['Query']> = {
currentUser?: Resolver<Maybe<ResolversTypes['User']>, ParentType, ContextType>;
hello?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
manyHellos?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType>;
manyHellosCount?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
};

export type UserResolvers<ContextType = any, ParentType extends ResolversParentTypes['User'] = ResolversParentTypes['User']> = {
Expand All @@ -169,6 +190,7 @@ export type UserResolvers<ContextType = any, ParentType extends ResolversParentT

export type Resolvers<ContextType = any> = {
Message?: MessageResolvers<ContextType>;
Mutation?: MutationResolvers<ContextType>;
Query?: QueryResolvers<ContextType>;
User?: UserResolvers<ContextType>;
};
Expand Down
Loading

0 comments on commit 66f4378

Please sign in to comment.