mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-06-02 21:49:25 +00:00
58838f225c
Rewrite of the UI using Typescript, Vue3, Windicss and Vite. The design should be close to the current one with some changes: - latest pipeline in a sidebar on the right - secrets and registry as part of the repo-settings (secrets and registry entries shouldn't be used as much so they can be "hidden" under settings IMO) - start page shows list of active repositories with button to enable / add new ones (currently you see all repositories and in most cases you only add new repositories once in a while)
137 lines
3.4 KiB
TypeScript
137 lines
3.4 KiB
TypeScript
export type ApiError = {
|
|
status: number;
|
|
message: string;
|
|
};
|
|
|
|
export function encodeQueryString(_params: Record<string, string | number | boolean | undefined> = {}): string {
|
|
const params: Record<string, string | number | boolean> = {};
|
|
|
|
Object.keys(_params).forEach((key) => {
|
|
const val = _params[key];
|
|
if (val !== undefined) {
|
|
params[key] = val;
|
|
}
|
|
});
|
|
|
|
return params
|
|
? Object.keys(params)
|
|
.sort()
|
|
.map((key) => {
|
|
const val = params[key];
|
|
return `${encodeURIComponent(key)}=${encodeURIComponent(val)}`;
|
|
})
|
|
.join('&')
|
|
: '';
|
|
}
|
|
|
|
export default class ApiClient {
|
|
server: string;
|
|
|
|
token: string | null;
|
|
|
|
csrf: string | null;
|
|
|
|
onerror: ((err: ApiError) => void) | undefined;
|
|
|
|
constructor(server: string, token: string | null, csrf: string | null) {
|
|
this.server = server;
|
|
this.token = token;
|
|
this.csrf = csrf;
|
|
}
|
|
|
|
private _request(method: string, path: string, data: unknown): Promise<unknown> {
|
|
const endpoint = `${this.server}${path}`;
|
|
const xhr = new XMLHttpRequest();
|
|
xhr.open(method, endpoint, true);
|
|
|
|
if (this.token) {
|
|
xhr.setRequestHeader('Authorization', `Bearer ${this.token}`);
|
|
}
|
|
|
|
if (method !== 'GET' && this.csrf) {
|
|
xhr.setRequestHeader('X-CSRF-TOKEN', this.csrf);
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
xhr.onload = () => {
|
|
if (xhr.readyState === 4) {
|
|
if (xhr.status >= 300) {
|
|
const error: ApiError = {
|
|
status: xhr.status,
|
|
message: xhr.response,
|
|
};
|
|
if (this.onerror) {
|
|
this.onerror(error);
|
|
}
|
|
reject(error);
|
|
return;
|
|
}
|
|
const contentType = xhr.getResponseHeader('Content-Type');
|
|
if (contentType && contentType.startsWith('application/json')) {
|
|
resolve(JSON.parse(xhr.response));
|
|
} else {
|
|
resolve(xhr.response);
|
|
}
|
|
}
|
|
};
|
|
|
|
xhr.onerror = (e) => {
|
|
reject(e);
|
|
};
|
|
|
|
if (data) {
|
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
|
xhr.send(JSON.stringify(data));
|
|
} else {
|
|
xhr.send();
|
|
}
|
|
});
|
|
}
|
|
|
|
_get(path: string) {
|
|
return this._request('GET', path, null);
|
|
}
|
|
|
|
_post(path: string, data?: unknown) {
|
|
return this._request('POST', path, data);
|
|
}
|
|
|
|
_patch(path: string, data?: unknown) {
|
|
return this._request('PATCH', path, data);
|
|
}
|
|
|
|
_delete(path: string) {
|
|
return this._request('DELETE', path, null);
|
|
}
|
|
|
|
_subscribe<T>(path: string, callback: (data: T) => void, opts = { reconnect: true }) {
|
|
const query = encodeQueryString({
|
|
access_token: this.token || undefined,
|
|
});
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
let _path = this.server ? this.server + path : path;
|
|
_path = this.token ? `${path}?${query}` : path;
|
|
|
|
const events = new EventSource(_path);
|
|
events.onmessage = (event) => {
|
|
const data = JSON.parse(event.data) as T;
|
|
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
|
callback(data);
|
|
};
|
|
|
|
if (!opts.reconnect) {
|
|
events.onerror = (err) => {
|
|
// TODO check if such events really have a data property
|
|
if ((err as Event & { data: string }).data === 'eof') {
|
|
events.close();
|
|
}
|
|
};
|
|
}
|
|
return events;
|
|
}
|
|
|
|
setErrorHandler(onerror: (err: ApiError) => void) {
|
|
this.onerror = onerror;
|
|
}
|
|
}
|