Browse Source
* initial conversion of STUFF to typescript * more stuff * update babel deps, include commonjs transform * update bundler & eslint configuration * eslint --fix * upgrade deps * update docs, build stuff, peripheral stuff --------- Co-authored-by: f0x <f0x@cthu.lu>pull/2257/head
84 changed files with 2403 additions and 1659 deletions
@ -1,8 +0,0 @@
|
||||
#!/bin/sh |
||||
|
||||
# this script is really just here because GoReleaser doesn't let |
||||
# you set env vars in your 'before' commands in the free version |
||||
|
||||
set -eu |
||||
|
||||
BUDO_BUILD=1 node web/source |
||||
@ -1,2 +1,5 @@
|
||||
node_modules |
||||
public/bundle.* |
||||
|
||||
# Built/Typescript-compiled settings files. |
||||
settings-js |
||||
|
||||
@ -1,70 +0,0 @@
|
||||
/* |
||||
GoToSocial |
||||
Copyright (C) GoToSocial Authors admin@gotosocial.org |
||||
SPDX-License-Identifier: AGPL-3.0-or-later |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Affero General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Affero General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Affero General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
"use strict"; |
||||
|
||||
const { createApi, fetchBaseQuery } = require("@reduxjs/toolkit/query/react"); |
||||
const { serialize: serializeForm } = require("object-to-formdata"); |
||||
|
||||
function instanceBasedQuery(args, api, extraOptions) { |
||||
const state = api.getState(); |
||||
const { instance, token } = state.oauth; |
||||
|
||||
if (args.baseUrl == undefined) { |
||||
args.baseUrl = instance; |
||||
} |
||||
|
||||
if (args.discardEmpty) { |
||||
if (args.body == undefined || Object.keys(args.body).length == 0) { |
||||
return { data: null }; |
||||
} |
||||
delete args.discardEmpty; |
||||
} |
||||
|
||||
if (args.asForm) { |
||||
delete args.asForm; |
||||
args.body = serializeForm(args.body, { |
||||
indices: true, // Array indices, for profile fields
|
||||
}); |
||||
} |
||||
|
||||
return fetchBaseQuery({ |
||||
baseUrl: args.baseUrl, |
||||
prepareHeaders: (headers) => { |
||||
if (token != undefined) { |
||||
headers.set('Authorization', token); |
||||
} |
||||
headers.set("Accept", "application/json"); |
||||
return headers; |
||||
}, |
||||
})(args, api, extraOptions); |
||||
} |
||||
|
||||
module.exports = createApi({ |
||||
reducerPath: "api", |
||||
baseQuery: instanceBasedQuery, |
||||
tagTypes: ["Auth", "Emoji", "Reports", "Account", "InstanceRules"], |
||||
endpoints: (build) => ({ |
||||
instance: build.query({ |
||||
query: () => ({ |
||||
url: `/api/v1/instance` |
||||
}) |
||||
}) |
||||
}) |
||||
}); |
||||
@ -0,0 +1,149 @@
|
||||
/* |
||||
GoToSocial |
||||
Copyright (C) GoToSocial Authors admin@gotosocial.org |
||||
SPDX-License-Identifier: AGPL-3.0-or-later |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Affero General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Affero General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Affero General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
|
||||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; |
||||
import type { |
||||
BaseQueryFn, |
||||
FetchArgs, |
||||
FetchBaseQueryError, |
||||
} from '@reduxjs/toolkit/query/react'; |
||||
import { serialize as serializeForm } from "object-to-formdata"; |
||||
|
||||
import type { RootState } from '../../redux/store'; |
||||
|
||||
/** |
||||
* GTSFetchArgs extends standard FetchArgs used by |
||||
* RTK Query with a couple helpers of our own. |
||||
*/ |
||||
export interface GTSFetchArgs extends FetchArgs { |
||||
/** |
||||
* If provided, will be used as base URL. Else, |
||||
* will fall back to authorized instance as baseUrl. |
||||
*/ |
||||
baseUrl?: string; |
||||
/** |
||||
* If true, and no args.body is set, or args.body is empty, |
||||
* then a null response will be returned from the API call.
|
||||
*/ |
||||
discardEmpty?: boolean; |
||||
/** |
||||
* If true, then args.body will be serialized |
||||
* as FormData before submission.
|
||||
*/ |
||||
asForm?: boolean; |
||||
} |
||||
|
||||
/** |
||||
* gtsBaseQuery wraps the redux toolkit fetchBaseQuery with some helper functionality. |
||||
*
|
||||
* For an explainer of what's happening in this function, see: |
||||
* - https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#customizing-queries-with-basequery
|
||||
* - https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#constructing-a-dynamic-base-url-using-redux-state
|
||||
*
|
||||
* @param args
|
||||
* @param api
|
||||
* @param extraOptions
|
||||
* @returns
|
||||
*/ |
||||
const gtsBaseQuery: BaseQueryFn< |
||||
string | GTSFetchArgs, |
||||
any, |
||||
FetchBaseQueryError |
||||
> = async (args, api, extraOptions) => { |
||||
// Retrieve state at the moment
|
||||
// this function was called.
|
||||
const state = api.getState() as RootState; |
||||
const { instanceUrl, token } = state.oauth; |
||||
|
||||
// Derive baseUrl dynamically.
|
||||
let baseUrl: string; |
||||
|
||||
// Check if simple string baseUrl provided
|
||||
// as args, or if more complex args provided.
|
||||
if (typeof args === "string") { |
||||
baseUrl = args; |
||||
} else { |
||||
if (args.baseUrl != undefined) { |
||||
baseUrl = args.baseUrl; |
||||
} else { |
||||
baseUrl = instanceUrl; |
||||
} |
||||
|
||||
if (args.discardEmpty) { |
||||
if (args.body == undefined || Object.keys(args.body).length == 0) { |
||||
return { data: null }; |
||||
} |
||||
} |
||||
|
||||
if (args.asForm) { |
||||
args.body = serializeForm(args.body, { |
||||
// Array indices, for profile fields.
|
||||
indices: true, |
||||
}); |
||||
} |
||||
|
||||
// Delete any of our extended arguments
|
||||
// to avoid confusing fetchBaseQuery.
|
||||
delete args.baseUrl; |
||||
delete args.discardEmpty; |
||||
delete args.asForm; |
||||
} |
||||
|
||||
if (!baseUrl) { |
||||
return { |
||||
error: { |
||||
status: 400, |
||||
statusText: 'Bad Request', |
||||
data: {"error":"No baseUrl set for request"}, |
||||
}, |
||||
}; |
||||
} |
||||
|
||||
return fetchBaseQuery({ |
||||
baseUrl: baseUrl, |
||||
prepareHeaders: (headers) => { |
||||
if (token != undefined) { |
||||
headers.set('Authorization', token); |
||||
} |
||||
headers.set("Accept", "application/json"); |
||||
return headers; |
||||
}, |
||||
})(args, api, extraOptions); |
||||
}; |
||||
|
||||
export const gtsApi = createApi({ |
||||
reducerPath: "api", |
||||
baseQuery: gtsBaseQuery, |
||||
tagTypes: [ |
||||
"Auth", |
||||
"Emoji", |
||||
"Reports", |
||||
"Account", |
||||
"InstanceRules", |
||||
], |
||||
endpoints: (builder) => ({ |
||||
instance: builder.query<any, void>({ |
||||
query: () => ({ |
||||
url: `/api/v1/instance` |
||||
}) |
||||
}) |
||||
}) |
||||
}); |
||||
|
||||
export const { useInstanceQuery } = gtsApi; |
||||
@ -1,160 +0,0 @@
|
||||
/* |
||||
GoToSocial |
||||
Copyright (C) GoToSocial Authors admin@gotosocial.org |
||||
SPDX-License-Identifier: AGPL-3.0-or-later |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Affero General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Affero General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Affero General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
"use strict"; |
||||
|
||||
const Promise = require("bluebird"); |
||||
|
||||
const base = require("./base"); |
||||
const { unwrapRes } = require("./lib"); |
||||
const oauth = require("../../redux/oauth").actions; |
||||
|
||||
function getSettingsURL() { |
||||
/* needed in case the settings interface isn't hosted at /settings but |
||||
some subpath like /gotosocial/settings. Other parts of the code don't |
||||
take this into account yet so mostly future-proofing. |
||||
|
||||
Also drops anything past /settings/, because authorization urls that are too long |
||||
get rejected by GTS. |
||||
*/ |
||||
let [pre, _past] = window.location.pathname.split("/settings"); |
||||
return `${window.location.origin}${pre}/settings`; |
||||
} |
||||
|
||||
const SETTINGS_URL = getSettingsURL(); |
||||
|
||||
const endpoints = (build) => ({ |
||||
verifyCredentials: build.query({ |
||||
providesTags: (_res, error) => |
||||
error == undefined |
||||
? ["Auth"] |
||||
: [], |
||||
queryFn: (_arg, api, _extraOpts, baseQuery) => { |
||||
const state = api.getState(); |
||||
|
||||
return Promise.try(() => { |
||||
// Process callback code first, if available
|
||||
if (state.oauth.loginState == "callback") { |
||||
let urlParams = new URLSearchParams(window.location.search); |
||||
let code = urlParams.get("code"); |
||||
|
||||
if (code == undefined) { |
||||
throw { |
||||
message: "Waiting for callback, but no ?code= provided in url." |
||||
}; |
||||
} else { |
||||
let app = state.oauth.registration; |
||||
|
||||
if (app == undefined || app.client_id == undefined) { |
||||
throw { |
||||
message: "No stored registration data, can't finish login flow." |
||||
}; |
||||
} |
||||
|
||||
return baseQuery({ |
||||
method: "POST", |
||||
url: "/oauth/token", |
||||
body: { |
||||
client_id: app.client_id, |
||||
client_secret: app.client_secret, |
||||
redirect_uri: SETTINGS_URL, |
||||
grant_type: "authorization_code", |
||||
code: code |
||||
} |
||||
}).then(unwrapRes).then((token) => { |
||||
// remove ?code= from url
|
||||
window.history.replaceState({}, document.title, window.location.pathname); |
||||
api.dispatch(oauth.setToken(token)); |
||||
}); |
||||
} |
||||
} |
||||
}).then(() => { |
||||
return baseQuery({ |
||||
url: `/api/v1/accounts/verify_credentials` |
||||
}); |
||||
}).catch((e) => { |
||||
return { error: e }; |
||||
}); |
||||
} |
||||
}), |
||||
authorizeFlow: build.mutation({ |
||||
queryFn: (formData, api, _extraOpts, baseQuery) => { |
||||
let instance; |
||||
const state = api.getState(); |
||||
|
||||
return Promise.try(() => { |
||||
if (!formData.instance.startsWith("http")) { |
||||
formData.instance = `https://${formData.instance}`; |
||||
} |
||||
instance = new URL(formData.instance).origin; |
||||
|
||||
const stored = state.oauth.instance; |
||||
if (stored?.instance == instance && stored.registration) { |
||||
return stored.registration; |
||||
} |
||||
|
||||
return baseQuery({ |
||||
method: "POST", |
||||
baseUrl: instance, |
||||
url: "/api/v1/apps", |
||||
body: { |
||||
client_name: "GoToSocial Settings", |
||||
scopes: formData.scopes, |
||||
redirect_uris: SETTINGS_URL, |
||||
website: SETTINGS_URL |
||||
} |
||||
}).then(unwrapRes).then((app) => { |
||||
app.scopes = formData.scopes; |
||||
|
||||
api.dispatch(oauth.authorize({ |
||||
instance: instance, |
||||
registration: app, |
||||
loginState: "callback", |
||||
expectingRedirect: true |
||||
})); |
||||
|
||||
return app; |
||||
}); |
||||
}).then((app) => { |
||||
let url = new URL(instance); |
||||
url.pathname = "/oauth/authorize"; |
||||
url.searchParams.set("client_id", app.client_id); |
||||
url.searchParams.set("redirect_uri", SETTINGS_URL); |
||||
url.searchParams.set("response_type", "code"); |
||||
url.searchParams.set("scope", app.scopes); |
||||
|
||||
let redirectURL = url.toString(); |
||||
window.location.assign(redirectURL); |
||||
|
||||
return { data: null }; |
||||
}).catch((e) => { |
||||
return { error: e }; |
||||
}); |
||||
}, |
||||
}), |
||||
logout: build.mutation({ |
||||
queryFn: (_arg, api) => { |
||||
api.dispatch(oauth.remove()); |
||||
return { data: null }; |
||||
}, |
||||
invalidatesTags: ["Auth"] |
||||
}) |
||||
}); |
||||
|
||||
module.exports = base.injectEndpoints({ endpoints }); |
||||
@ -0,0 +1,204 @@
|
||||
/* |
||||
GoToSocial |
||||
Copyright (C) GoToSocial Authors admin@gotosocial.org |
||||
SPDX-License-Identifier: AGPL-3.0-or-later |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Affero General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Affero General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Affero General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
|
||||
import type { FetchBaseQueryError } from '@reduxjs/toolkit/query'; |
||||
|
||||
import { gtsApi } from "../gts-api"; |
||||
import { |
||||
setToken as oauthSetToken, |
||||
remove as oauthRemove, |
||||
authorize as oauthAuthorize, |
||||
} from "../../../redux/oauth"; |
||||
import { RootState } from '../../../redux/store'; |
||||
|
||||
export interface OauthTokenRequestBody { |
||||
client_id: string; |
||||
client_secret: string; |
||||
redirect_uri: string; |
||||
grant_type: string; |
||||
code: string; |
||||
} |
||||
|
||||
function getSettingsURL() { |
||||
/* |
||||
needed in case the settings interface isn't hosted at /settings but |
||||
some subpath like /gotosocial/settings. Other parts of the code don't |
||||
take this into account yet so mostly future-proofing. |
||||
|
||||
Also drops anything past /settings/, because authorization urls that are too long |
||||
get rejected by GTS. |
||||
*/ |
||||
let [pre, _past] = window.location.pathname.split("/settings"); |
||||
return `${window.location.origin}${pre}/settings`; |
||||
} |
||||
|
||||
const SETTINGS_URL = (getSettingsURL()); |
||||
|
||||
// Couple auth functions here require multiple requests as
|
||||
// part of an OAuth token 'flow'. To keep things simple for
|
||||
// callers of these query functions, the multiple requests
|
||||
// are chained within one query.
|
||||
//
|
||||
// https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#performing-multiple-requests-with-a-single-query
|
||||
const extended = gtsApi.injectEndpoints({ |
||||
endpoints: (builder) => ({ |
||||
verifyCredentials: builder.query<any, void>({ |
||||
providesTags: (_res, error) => |
||||
error == undefined ? ["Auth"] : [], |
||||
async queryFn(_arg, api, _extraOpts, fetchWithBQ) { |
||||
const state = api.getState() as RootState; |
||||
const oauthState = state.oauth; |
||||
|
||||
// If we're not in the middle of an auth/callback,
|
||||
// we may already have an auth token, so just
|
||||
// return a standard verify_credentials query.
|
||||
if (oauthState.loginState != 'callback') { |
||||
return fetchWithBQ({ |
||||
url: `/api/v1/accounts/verify_credentials` |
||||
}); |
||||
} |
||||
|
||||
// We're in the middle of an auth/callback flow.
|
||||
// Try to retrieve callback code from URL query.
|
||||
let urlParams = new URLSearchParams(window.location.search); |
||||
let code = urlParams.get("code"); |
||||
if (code == undefined) { |
||||
return { |
||||
error: { |
||||
status: 400, |
||||
statusText: 'Bad Request', |
||||
data: {"error":"Waiting for callback, but no ?code= provided in url."}, |
||||
}, |
||||
}; |
||||
} |
||||
|
||||
// Retrieve app with which the
|
||||
// callback code was generated.
|
||||
let app = oauthState.app; |
||||
if (app == undefined || app.client_id == undefined) { |
||||
return { |
||||
error: { |
||||
status: 400, |
||||
statusText: 'Bad Request', |
||||
data: {"error":"No stored app registration data, can't finish login flow."}, |
||||
}, |
||||
}; |
||||
} |
||||
|
||||
// Use the provided code and app
|
||||
// secret to request an auth token.
|
||||
const tokenReqBody: OauthTokenRequestBody = { |
||||
client_id: app.client_id, |
||||
client_secret: app.client_secret, |
||||
redirect_uri: SETTINGS_URL, |
||||
grant_type: "authorization_code", |
||||
code: code |
||||
}; |
||||
|
||||
const tokenResult = await fetchWithBQ({ |
||||
method: "POST", |
||||
url: "/oauth/token", |
||||
body: tokenReqBody, |
||||
}); |
||||
if (tokenResult.error) { |
||||
return { error: tokenResult.error as FetchBaseQueryError }; |
||||
} |
||||
|
||||
// Remove ?code= query param from
|
||||
// url, we don't want it anymore.
|
||||
window.history.replaceState({}, document.title, window.location.pathname); |
||||
|
||||
// Store returned token in redux.
|
||||
api.dispatch(oauthSetToken(tokenResult.data)); |
||||
|
||||
// We're now authed! So return
|
||||
// standard verify_credentials query.
|
||||
return fetchWithBQ({ |
||||
url: `/api/v1/accounts/verify_credentials` |
||||
}); |
||||
} |
||||
}), |
||||
|
||||
authorizeFlow: builder.mutation({ |
||||
async queryFn(formData, api, _extraOpts, fetchWithBQ) { |
||||
const state = api.getState() as RootState; |
||||
const oauthState = state.oauth; |
||||
|
||||
let instanceUrl: string; |
||||
if (!formData.instance.startsWith("http")) { |
||||
formData.instance = `https://${formData.instance}`; |
||||
} |
||||
|
||||
instanceUrl = new URL(formData.instance).origin; |
||||
if (oauthState?.instanceUrl == instanceUrl && oauthState.app) { |
||||
return { data: oauthState.app }; |
||||
} |
||||
|
||||
const appResult = await fetchWithBQ({ |
||||
method: "POST", |
||||
baseUrl: instanceUrl, |
||||
url: "/api/v1/apps", |
||||
body: { |
||||
client_name: "GoToSocial Settings", |
||||
scopes: formData.scopes, |
||||
redirect_uris: SETTINGS_URL, |
||||
website: SETTINGS_URL |
||||
} |
||||
}); |
||||
if (appResult.error) { |
||||
return { error: appResult.error as FetchBaseQueryError }; |
||||
} |
||||
|
||||
let app = appResult.data as any; |
||||
|
||||
app.scopes = formData.scopes; |
||||
api.dispatch(oauthAuthorize({ |
||||
instanceUrl: instanceUrl, |
||||
app: app, |
||||
loginState: "callback", |
||||
expectingRedirect: true |
||||
})); |
||||
|
||||
let url = new URL(instanceUrl); |
||||
url.pathname = "/oauth/authorize"; |
||||
url.searchParams.set("client_id", app.client_id); |
||||
url.searchParams.set("redirect_uri", SETTINGS_URL); |
||||
url.searchParams.set("response_type", "code"); |
||||
url.searchParams.set("scope", app.scopes); |
||||
|
||||
let redirectURL = url.toString(); |
||||
window.location.assign(redirectURL); |
||||
return { data: null }; |
||||
}, |
||||
}), |
||||
logout: builder.mutation({ |
||||
queryFn: (_arg, api) => { |
||||
api.dispatch(oauthRemove()); |
||||
return { data: null }; |
||||
}, |
||||
invalidatesTags: ["Auth"] |
||||
}) |
||||
}) |
||||
}); |
||||
|
||||
export const { |
||||
useVerifyCredentialsQuery, |
||||
useAuthorizeFlowMutation, |
||||
useLogoutMutation, |
||||
} = extended; |
||||
@ -1,44 +0,0 @@
|
||||
/* |
||||
GoToSocial |
||||
Copyright (C) GoToSocial Authors admin@gotosocial.org |
||||
SPDX-License-Identifier: AGPL-3.0-or-later |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Affero General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Affero General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Affero General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
"use strict"; |
||||
|
||||
const { createSlice } = require("@reduxjs/toolkit"); |
||||
|
||||
module.exports = createSlice({ |
||||
name: "oauth", |
||||
initialState: { |
||||
loginState: 'none', |
||||
expectingRedirect: false |
||||
}, |
||||
reducers: { |
||||
authorize: (state, { payload }) => { |
||||
return payload; // overrides state
|
||||
}, |
||||
setToken: (state, { payload }) => { |
||||
state.token = `${payload.token_type} ${payload.access_token}`; |
||||
state.loginState = "login"; |
||||
}, |
||||
remove: (state, { _payload }) => { |
||||
delete state.token; |
||||
delete state.registration; |
||||
state.loginState = "logout"; |
||||
} |
||||
} |
||||
}); |
||||
@ -0,0 +1,89 @@
|
||||
/* |
||||
GoToSocial |
||||
Copyright (C) GoToSocial Authors admin@gotosocial.org |
||||
SPDX-License-Identifier: AGPL-3.0-or-later |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Affero General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Affero General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Affero General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit"; |
||||
|
||||
/** |
||||
* OAuthToken represents a response |
||||
* to an OAuth token request. |
||||
*/ |
||||
export interface OAuthToken { |
||||
/** |
||||
* Most likely to be 'Bearer' |
||||
* but may be something else. |
||||
*/ |
||||
token_type: string; |
||||
/** |
||||
* The actual token. Can be passed in to |
||||
* authenticate further requests using the |
||||
* Authorization header and the token type. |
||||
*/ |
||||
access_token: string; |
||||
} |
||||
|
||||
export interface OAuthApp { |
||||
client_id: string; |
||||
client_secret: string; |
||||
} |
||||
|
||||
export interface OAuthState { |
||||
instanceUrl?: string; |
||||
loginState: "none" | "callback" | "login" | "logout"; |
||||
expectingRedirect: boolean; |
||||
/** |
||||
* Token stored in easy-to-use format. |
||||
* Will look something like: |
||||
* "Authorization: Bearer BLAHBLAHBLAH" |
||||
*/ |
||||
token?: string; |
||||
app?: OAuthApp; |
||||
} |
||||
|
||||
const initialState: OAuthState = { |
||||
loginState: 'none', |
||||
expectingRedirect: false, |
||||
}; |
||||
|
||||
export const oauthSlice = createSlice({ |
||||
name: "oauth", |
||||
initialState: initialState, |
||||
reducers: { |
||||
authorize: (_state, action: PayloadAction<OAuthState>) => { |
||||
// Overrides state with payload.
|
||||
return action.payload; |
||||
}, |
||||
setToken: (state, action: PayloadAction<OAuthToken>) => { |
||||
// Mark us as logged in by storing token.
|
||||
state.token = `${action.payload.token_type} ${action.payload.access_token}`; |
||||
state.loginState = "login"; |
||||
}, |
||||
remove: (state) => { |
||||
// Mark us as logged out by clearing auth.
|
||||
delete state.token; |
||||
delete state.app; |
||||
state.loginState = "logout"; |
||||
} |
||||
} |
||||
}); |
||||
|
||||
export const { |
||||
authorize, |
||||
setToken, |
||||
remove, |
||||
} = oauthSlice.actions; |
||||
@ -0,0 +1,109 @@
|
||||
{ |
||||
"compilerOptions": { |
||||
/* Visit https://aka.ms/tsconfig to read more about this file */ |
||||
|
||||
/* Projects */ |
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ |
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ |
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ |
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ |
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ |
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ |
||||
|
||||
/* Language and Environment */ |
||||
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ |
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ |
||||
"jsx": "react" , /* Specify what JSX code is generated. */ |
||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ |
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ |
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ |
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ |
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ |
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ |
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ |
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ |
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ |
||||
|
||||
/* Modules */ |
||||
"module": "commonjs", /* Specify what module code is generated. */ |
||||
// "rootDir": "./", /* Specify the root folder within your source files. */ |
||||
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ |
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ |
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ |
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ |
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ |
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */ |
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ |
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ |
||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ |
||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ |
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ |
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ |
||||
// "resolveJsonModule": true, /* Enable importing .json files. */ |
||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ |
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ |
||||
|
||||
/* JavaScript Support */ |
||||
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ |
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ |
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ |
||||
|
||||
/* Emit */ |
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ |
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */ |
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ |
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */ |
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ |
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ |
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */ |
||||
// "removeComments": true, /* Disable emitting comments. */ |
||||
"noEmit": true, /* Disable emitting files from a compilation. */ |
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ |
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ |
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ |
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ |
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ |
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ |
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ |
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */ |
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ |
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ |
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ |
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ |
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */ |
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ |
||||
|
||||
/* Interop Constraints */ |
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ |
||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ |
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ |
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ |
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ |
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ |
||||
|
||||
/* Type Checking */ |
||||
"strict": false, /* Enable all strict type-checking options. */ |
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ |
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ |
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ |
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ |
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ |
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ |
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ |
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ |
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ |
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ |
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ |
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ |
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ |
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ |
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ |
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ |
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ |
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ |
||||
|
||||
/* Completeness */ |
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ |
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */ |
||||
} |
||||
} |
||||
Loading…
Reference in new issue