Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
28c643c
[fix] Activity lists with User Activity model
TechQuery May 3, 2026
6239405
Replace local Award with Award type from @freecodecamp-chengdu/hop-se…
Copilot May 5, 2026
d292915
Address review: explicit target fallback and consistent award variabl…
Copilot May 5, 2026
62be8ee
Replace local Question with hop-service type and add preset fill button
Copilot May 5, 2026
c82a700
Add confirmation guard when filling with defaults overwrites existing…
Copilot May 5, 2026
0dca77b
Fix Award/Question imports: remove re-exports, use import type and st…
Copilot May 5, 2026
ea3060d
Add /user/signIn OAuth page and refactor sessionGuard to redirect whe…
Copilot May 7, 2026
ab2b4a6
Fix: extract shared OAuth scopes constant and URI-encode client_id
Copilot May 7, 2026
ea6e668
Fix signIn page: auth chain only on code param, flatten JSX, function…
Copilot May 7, 2026
c9192b0
Move OAuth URL build to page component, use buildURLData, compose ins…
Copilot May 7, 2026
82aab62
Fix scope separator to comma, remove as any in favor of explicit redi…
Copilot May 7, 2026
39f0061
Add CNB OAuth page, cnbSigner middleware, signInWithCNB, and CNB butt…
Copilot May 8, 2026
9fb830f
Refactor CNB OAuth: validate via CNB API, store in cookie, move logic…
Copilot May 8, 2026
9997ad8
[refactor] simplify Sign In middlewares with multiple OAuth pages
TechQuery May 8, 2026
e3d1590
security: prevent open redirect and CNB token leakage in OAuth pages
Copilot May 9, 2026
4b994e0
[migrate] upgrade to PNPM 11, Lint-Staged 17 & other latest Upstream …
TechQuery May 9, 2026
d3508e0
feat: add CNB workspace creation to GitModel.createOneFrom
Copilot May 9, 2026
3e04487
[fix] Vercel & PNPM compatibility
TechQuery May 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
public-hoist-pattern[] = *import-in-the-middle*
public-hoist-pattern[] = *require-in-the-middle*
auto-install-peers = false
5 changes: 3 additions & 2 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"useShorthandAssign": "error",
"useSingleVarDeclarator": "off",
"useImportType": "off",
"useTemplate": "error",
"useTemplate": "warn",
"noImplicitBoolean": "off",
"noInferrableTypes": "error",
"noNegationElse": "error",
Expand Down Expand Up @@ -72,7 +72,8 @@
"noAccumulatingSpread": "warn"
},
"security": {
"noGlobalEval": "error"
"noGlobalEval": "error",
"noDangerouslySetInnerHtml": "warn"
},
"complexity": {
"noCommaOperator": "error"
Expand Down
5 changes: 3 additions & 2 deletions components/Activity/ActivityList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ActivityCard, ActivityCardProps } from './ActivityCard';
export interface ActivityListLayoutProps
extends XScrollListProps<Hackathon>,
Pick<ActivityCardProps, 'onPublish' | 'onDelete'> {
store?: ActivityModel;
type?: ActivityListType;
size?: 'sm' | 'lg';
userId?: number;
Expand All @@ -29,7 +30,7 @@ export const ActivityListLayout: FC<ActivityListLayoutProps> = ({
className="g-4"
xs={1}
sm={2}
{...(size === 'sm' ? {} : !size ? { lg: 3, xxl: 4 } : { lg: 4, xxl: 6 })}
{...(size === 'sm' ? {} : size ? { lg: 4, xxl: 6 } : { lg: 3, xxl: 4 })}
>
{defaultData.map(item => (
<Col key={item.name + item.id}>
Expand All @@ -49,7 +50,7 @@ export const ActivityListLayout: FC<ActivityListLayoutProps> = ({
export type ActivityListProps = ActivityListLayoutProps;

export default class ActivityList extends PureComponent<ActivityListProps> {
store = new ActivityModel();
store = this.props.store || new ActivityModel();

Comment on lines +53 to 54
componentDidMount() {
if (this.props.type === 'admin' && !platformAdmin.isPlatformAdmin)
Expand Down
5 changes: 3 additions & 2 deletions components/Activity/AwardList.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { Award } from '@freecodecamp-chengdu/hop-service';
import { Loading } from 'idea-react';
import { computed } from 'mobx';
import { observer } from 'mobx-react';
import { ObservedComponent } from 'mobx-react-helper';
import { Column, RestTable } from 'mobx-restful-table';
import { Image } from 'react-bootstrap';

import { Award, AwardModel } from '../../models/Activity/Award';
import { AwardModel } from '../../models/Activity/Award';
import { i18n, I18nContext } from '../../models/Base/Translation';

export const AwardTargetName = ({ t }: typeof i18n) => ({
Expand All @@ -27,7 +28,7 @@ export class AwardList extends ObservedComponent<{ store: AwardModel }, typeof i
{
key: 'target',
renderHead: t('type'),
renderBody: ({ target }) => AwardTargetName(i18n)[target],
renderBody: ({ target }) => target ? (AwardTargetName(i18n)[target] ?? '') : '',
},
{
key: 'pictures',
Expand Down
6 changes: 3 additions & 3 deletions components/Activity/QuestionnaireCreate.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Question } from '@freecodecamp-chengdu/hop-service';
import type { Question } from '@freecodecamp-chengdu/hop-service';
import { observer } from 'mobx-react';
import { BadgeInput } from 'mobx-restful-table';
import { FC, FormEvent, useContext } from 'react';
import { FC, SubmitEvent, useContext } from 'react';
import { Button, Col, Form, Row } from 'react-bootstrap';
import { formToJSON } from 'web-utility';

Expand All @@ -18,7 +18,7 @@ export interface QuestionnaireCreateProps {
export const QuestionnaireCreate: FC<QuestionnaireCreateProps> = observer(({ onAdd }) => {
const { t } = useContext(I18nContext);

const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
const handleSubmit = (event: SubmitEvent<HTMLFormElement>) => {
event.preventDefault();
event.stopPropagation();

Expand Down
3 changes: 1 addition & 2 deletions components/Activity/QuestionnairePreview.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { Question } from '@freecodecamp-chengdu/hop-service';
import { observer } from 'mobx-react';
import { ObservedComponent } from 'mobx-react-helper';
import { Container, Form, Row } from 'react-bootstrap';

import { Question } from '../../models/Activity/Question';
import { i18n, I18nContext } from '../../models/Base/Translation';

export interface QuestionnaireFormProps {
Expand Down
5 changes: 2 additions & 3 deletions components/Activity/QuestionnaireTable.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { Question, QuestionType } from '@freecodecamp-chengdu/hop-service';
import { observer } from 'mobx-react';
import { FC, useContext } from 'react';
import { Button, Container, Table } from 'react-bootstrap';

import { Question } from '../../models/Activity/Question';
import { i18n, I18nContext } from '../../models/Base/Translation';

export interface QuestionnaireTableProps {
Expand Down Expand Up @@ -35,7 +34,7 @@ export const QuestionnaireTable: FC<QuestionnaireTableProps> = observer(
</thead>
<tbody>
{questionnaire.map(
({ id, title, options, multiple, type = 'text', required }, index, { length }) => (
({ id, title, options, multiple, type = 'text' as QuestionType, required }, index, { length }) => (
<tr key={id}>
<td>{index + 1}</td>
<td>{id}</td>
Expand Down
9 changes: 4 additions & 5 deletions components/Git/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FormEvent } from 'react';
import { Button, Col, Form, FormGroup, Modal, ModalProps, Row } from 'react-bootstrap';
import { formToJSON } from 'web-utility';

import { i18n, I18nContext } from '../../models/Base/Translation';
import { I18nContext, i18n } from '../../models/Base/Translation';
import { GitModel } from '../../models/Git';

export interface GitModalProps extends Pick<ModalProps, 'show' | 'onHide'> {
Expand Down Expand Up @@ -71,10 +71,9 @@ export class GitModal extends ObservedComponent<GitModalProps, typeof i18n> {
name="html_url"
value={value}
required
placeholder={t(
'for_example',
'https://github.com/idea2app/React-MobX-Bootstrap-ts',
)}
placeholder={t('for_example', {
example: 'https://github.com/idea2app/React-MobX-Bootstrap-ts',
})}
onChange={({ currentTarget: { value } }) => (this.value = value)}
/>
</Col>
Expand Down
19 changes: 8 additions & 11 deletions components/Team/TeamAwardAssignment.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { AwardAssignment } from '@freecodecamp-chengdu/hop-service';
import { FC } from 'react';

import { AwardAssignment } from '../../models/Activity/Award';
import { XScrollListProps } from '../layout/ScrollList';

export interface TeamAwardAssignmentLayoutProps extends XScrollListProps<AwardAssignment> {
Expand All @@ -11,13 +10,11 @@ export interface TeamAwardAssignmentLayoutProps extends XScrollListProps<AwardAs
export const TeamAwardAssignmentLayout: FC<TeamAwardAssignmentLayoutProps> = ({
defaultData = [],
}) => (
<>
<ol>
{defaultData.map(({ updatedAt, id, description, award: { name } }) => (
<li key={id} className="list-unstyled">
{name}
</li>
))}
</ol>
</>
<ol>
{defaultData.map(({ id, award: { name } }) => (
<li key={id} className="list-unstyled">
{name}
</li>
))}
</ol>
);
2 changes: 1 addition & 1 deletion components/User/UserBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const UserBar = observer(() => {
</Dropdown.Menu>
</Dropdown>
) : (
<Button variant="outline-light" href="/user/me">
<Button variant="outline-light" href="/user/signIn">
{t('sign_in')}
</Button>
)}
Expand Down
18 changes: 1 addition & 17 deletions models/Activity/Award.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,9 @@
import { User, Base, Media, Team } from '@freecodecamp-chengdu/hop-service';
import type { Award, AwardAssignment } from '@freecodecamp-chengdu/hop-service';
import { ListModel, Stream, toggle } from 'mobx-restful';

import { createListStream, InputData, TableModel } from '../Base';
import sessionStore from '../User/Session';

export interface Award extends Record<'hackathonName' | 'name' | 'description', string>, Base {
quantity: number;
target: 'team' | 'individual';
pictures: Media[];
}

export interface AwardAssignment
extends
Omit<Base, 'id'>,
Omit<Award, 'name' | 'quantity' | 'target' | 'pictures'>,
Record<'assignmentId' | 'assigneeId' | 'awardId', number> {
user?: User;
team?: Team;
award: Award;
}

export class AwardModel extends TableModel<Award> {
client = sessionStore.client;
currentAssignment?: AwardAssignmentModel;
Expand Down
21 changes: 7 additions & 14 deletions models/Activity/Question.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import { i18n } from '../Base/Translation';
import type { Question, QuestionType } from '@freecodecamp-chengdu/hop-service';

export interface Question {
id?: string;
title: string;
options?: string[];
multiple?: boolean;
type?: 'text' | 'url';
required?: boolean;
}
import { i18n } from '../Base/Translation';

export const questions = ({ t }: typeof i18n): Question[] => [
{
Expand Down Expand Up @@ -48,19 +41,19 @@ export const questions = ({ t }: typeof i18n): Question[] => [
},
{
title: t('linkein_or_cv'),
type: 'url',
type: 'url' as QuestionType,
},
Comment on lines 42 to 45
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Typo: linkein_or_cv should be linkedin_or_cv.

The translation key (and presumably the entries in translation/en-US.ts/zh-CN.ts/zh-TW.ts) use linkein instead of linkedin. Easy to fix now before it becomes a load-bearing string in stored data or analytics.

✏️ Proposed fix (apply across translation files too)
-    title: t('linkein_or_cv'),
+    title: t('linkedin_or_cv'),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
title: t('linkein_or_cv'),
type: 'url',
type: 'url' as QuestionType,
},
{
title: t('linkedin_or_cv'),
type: 'url' as QuestionType,
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@models/Activity/Question.ts` around lines 42 - 45, In
models/Activity/Question.ts replace the typo'd translation key
t('linkein_or_cv') with t('linkedin_or_cv') and update all corresponding
translation entries (e.g., translation/en-US.ts, zh-CN.ts, zh-TW.ts) to
add/rename the key to "linkedin_or_cv" so the UI and stored analytics use the
corrected identifier; ensure you remove or alias the old "linkein_or_cv" key to
avoid duplicates.

{
title: t('social_media_account_or_twitter_or_weibo'),
type: 'url',
type: 'url' as QuestionType,
},
{
title: t('which_of_these_particular_areas_are_you_interested_in_going_into_this_event'),
type: 'text',
type: 'text' as QuestionType,
},
{
title: t('what_do_you_hope_to_learn_from_the_workshops'),
type: 'text',
type: 'text' as QuestionType,
},
{
title: t('do_you_plan__on_hacking_solo_or_with_a_team'),
Expand All @@ -72,6 +65,6 @@ export const questions = ({ t }: typeof i18n): Question[] => [
},
{
title: t('anything_else_we_should_know_about_what_youre_looking_for_in_a_team_or_teammate'),
type: 'text',
type: 'text' as QuestionType,
},
];
4 changes: 2 additions & 2 deletions models/Activity/Team.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
import type {
AwardAssignment,
BaseFilter,
Team as _Team,
TeamMember,
Expand All @@ -15,7 +16,6 @@ import { isServer } from '../../configuration';
import { createListStream, Filter, InputData, TableModel } from '../Base';
import { WorkspaceModel } from '../Git';
import sessionStore from '../User/Session';
import { AwardAssignment } from './Award';
import { EvaluationModel } from './Evaluation';

export interface Team extends _Team {
Expand Down
23 changes: 21 additions & 2 deletions models/Activity/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import {
import type {
Enrollment,
Hackathon,
HackathonFilter,
HackathonStatus,
ListChunk,
Question,
Questionnaire,
} from '@freecodecamp-chengdu/hop-service';
import { action, observable } from 'mobx';
import { persist, restore, toggle } from 'mobx-restful';
import { buildURLData } from 'web-utility';

import { isServer } from '../../configuration';
import { Filter, InputData, TableModel } from '../Base';
Expand Down Expand Up @@ -158,10 +161,26 @@ export class ActivityModel extends TableModel<Hackathon, ActivityFilter> {

@toggle('uploading')
async signOne(name: string, form: Enrollment['form'] = []) {
await this.client.put(`${this.baseURI}/${name}/enrollment`, { form });
await this.client.post(`${this.baseURI}/${name}/enrollment`, { form });

return this.currentEnrollment?.getSessionOne();
}
Comment on lines 163 to 167
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify all callers of signOne and whether currentEnrollment is guaranteed to be initialized first.
rg -nP -C3 '\bsignOne\s*\('
rg -nP -C3 '\benrollmentOf\s*\('

Repository: FreeCodeCamp-Chengdu/HOP

Length of output: 5175


🏁 Script executed:

# Check the register.tsx file to see if enrollmentOf is called before signOne
head -100 pages/activity/[name]/register.tsx

Repository: FreeCodeCamp-Chengdu/HOP

Length of output: 2698


🏁 Script executed:

# Check the full context of the index.tsx file where signOne is called
sed -n '60,130p' pages/activity/[name]/index.tsx

Repository: FreeCodeCamp-Chengdu/HOP

Length of output: 1936


🏁 Script executed:

# Check the getSessionOne implementation in EnrollmentModel
rg -nP -A5 'getSessionOne\s*\(' models/

Repository: FreeCodeCamp-Chengdu/HOP

Length of output: 1242


🏁 Script executed:

# Check the getOne method in Activity model to see the full initialization flow
sed -n '110,140p' models/Activity/index.ts

Repository: FreeCodeCamp-Chengdu/HOP

Length of output: 924


🏁 Script executed:

# Check EnrollmentModel constructor and class definition
sed -n '1,50p' models/Activity/Enrollment.ts

Repository: FreeCodeCamp-Chengdu/HOP

Length of output: 1622


🏁 Script executed:

# Check if there's any watch or auto-fetch in the model
rg -nP 'makeAutoObservable|makeObservable|reaction|watch' models/Activity/Enrollment.ts

Repository: FreeCodeCamp-Chengdu/HOP

Length of output: 50


The POST enrollment response is discarded and may return undefined in some call paths.

signOne discards the POST response and calls currentEnrollment?.getSessionOne() instead. Two issues:

  1. Silent failure in register.tsx: The page's server-side getOne() is called on a local ActivityModel instance, not the global activityStore. When the client calls signOne(), currentEnrollment is undefined on the global store, so the return silently fails. The enrollmentOf() method only creates a new model without fetching data.

  2. Wasteful redundant fetch: The POST response is discarded and a separate GET request is made via getSessionOne(). This is wasteful and potentially racy depending on backend semantics.

The index.tsx page is safer (calls enrollmentOf() before signOne()), but still depends on getSessionOne() being called elsewhere to populate sessionOne. Confirm the POST enrollment endpoint already returns the new enrollment data, and either return it directly or defer the GET until actually needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@models/Activity/index.ts` around lines 162 - 166, The signOne method
currently discards the POST response and returns
currentEnrollment?.getSessionOne(), which can be undefined when
currentEnrollment is not set (see enrollmentOf usage) and causes a
redundant/racy GET via getSessionOne(); update signOne to capture and return the
POST response body (the newly created enrollment/session data) and also update
the model state (set currentEnrollment or sessionOne on the ActivityModel) from
that response so callers get the authoritative data without an extra GET; ensure
you check the enrollment POST response shape and map it into the model (or
fallback to calling getSessionOne only if the response lacks the expected data).

}

export class UserActivityModel extends ActivityModel {
constructor(
public userId: number,
public role: 'creator' | 'staff' | 'enrollee',
) {
super();
}
Comment on lines +170 to +176
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

UserActivityModel inherits restore(this, 'Activity') — all instances share the same localStorage key as the singleton

ActivityModel line 37 defines restored = !isServer() && restore(this, 'Activity') as a class field initializer. Because UserActivityModel calls super(), that initializer runs for every instance, binding each one to the same 'Activity' localStorage key used by the export default new ActivityModel() singleton (line 185).

Consequences:

  • When UserActivityModel.loadPage stores its paginated result, mobx-restful's restore reaction writes to 'Activity', overwriting the singleton's cache.
  • Any singleton read that later triggers a restore reload (e.g., navigation back to the main activity list) may re-hydrate with the stale user-filtered list.
  • Multiple UserActivityModel instances (enrollee / creator / staff) overwrite each other on the same key.

Override restored in the subclass to opt out of persistence:

🛡️ Proposed fix
 export class UserActivityModel extends ActivityModel {
+  override restored = false as const;
+
   constructor(
     public userId: number,
     public role: 'creator' | 'staff' | 'enrollee',
   ) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@models/Activity/index.ts` around lines 169 - 175, The subclass
UserActivityModel is inheriting ActivityModel's restored field (which runs
restore(this, 'Activity') during super()), causing all instances to share the
singleton's localStorage key; to fix, override the restored field in
UserActivityModel to opt out of persistence by adding a class field like
restored = false (so after super() runs the subclass resets it), ensuring
UserActivityModel instances do not call restore(this, 'Activity') and therefore
don't overwrite the singleton cache—update UserActivityModel (constructor/class
body) to define restored = false and leave ActivityModel.restore logic
unchanged.


async loadPage(pageIndex: number, pageSize: number, filter: HackathonFilter) {
const { body } = await this.client.get<ListChunk<Hackathon>>(
`user/${this.userId}/hackathon/${this.role}?${buildURLData({ ...filter, pageIndex, pageSize })}`,
);
return { pageData: body!.list, totalCount: body!.count };
}
}

export default new ActivityModel();
44 changes: 42 additions & 2 deletions models/Git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,50 @@ export class GitModel extends TableModel<GitTemplate> {
}

@toggle('uploading')
async createOneFrom(templateURI: string, name: string) {
async createOneFrom(templateURI: string, name: string, cnbToken?: string) {
const { body } = await githubClient.post<Repository>(`repos/${templateURI}/generate`, { name });
const repo = body!;

return body!;
if (cnbToken) await this.createCNBWorkspace(repo.html_url, repo.name, cnbToken);

return repo;
}

async createCNBWorkspace(githubRepoURL: string, name: string, cnbToken: string) {
const cnbHeaders = {
Authorization: `Bearer ${cnbToken}`,
'Content-Type': 'application/json',
Accept: 'application/vnd.cnb.api+json',
};

const userResp = await fetch('https://api.cnb.cool/user', { headers: cnbHeaders });
if (!userResp.ok) throw new Error(`CNB authentication failed: ${userResp.status}`);
const { username } = (await userResp.json()) as { username: string };

const repoResp = await fetch(`https://api.cnb.cool/${username}/-/repos`, {
method: 'POST',
headers: cnbHeaders,
body: JSON.stringify({ name, visibility: 'private' }),
});
if (!repoResp.ok) throw new Error(`CNB repo creation failed: ${repoResp.status}`);
const repoPath = `${username}/${name}`;

const pushResp = await fetch('/api/git/cnb-push', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ repoPath, githubRepoURL, cnbToken }),
});
if (!pushResp.ok) throw new Error(`CNB git push failed: ${pushResp.status}`);

const workspaceResp = await fetch(`https://api.cnb.cool/${repoPath}/-/workspace/start`, {
method: 'POST',
headers: cnbHeaders,
body: JSON.stringify({ branch: 'main' }),
});
if (!workspaceResp.ok) throw new Error(`CNB workspace start failed: ${workspaceResp.status}`);
const { url } = (await workspaceResp.json()) as { url: string };

return { repoPath, workspaceURL: url };
}

@toggle('uploading')
Expand Down
7 changes: 7 additions & 0 deletions models/User/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ export class SessionModel extends BaseModel {
return body!;
}

static async signInWithCNB(accessToken: string) {
const { body } = await ownClient.post<User>('user/OAuth/CNB', {
accessToken,
});
return body!;
}

async signOut(reload = false) {
setCookie('token', '', { path: '/', expires: new Date() });
setCookie('JWT', '', { path: '/', expires: new Date() });
Expand Down
2 changes: 1 addition & 1 deletion models/User/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class UserModel extends TableModel<User, UserFilter> {
baseURI = 'user';

async getUserTopList() {
const { body } = await this.client.get<UserRankListChunk>(`activity-log/user-rank`);
const { body } = await this.client.get<UserRankListChunk>('activity-log/user-rank');
return body!.list;
}
}
Expand Down
Loading
Loading