add remove method for aliases
This commit is contained in:
parent
fb29813345
commit
70d95be227
16
index.ts
16
index.ts
@ -1,11 +1,13 @@
|
|||||||
import { app } from './app'
|
import { app } from "./app";
|
||||||
import { logger } from './logger'
|
import { logger } from "./logger";
|
||||||
|
|
||||||
const port = app.get('port')
|
const port = app.get("port");
|
||||||
const host = app.get('host')
|
const host = app.get("host");
|
||||||
|
|
||||||
process.on('unhandledRejection', (reason) => logger.error('Unhandled Rejection %O', reason))
|
process.on("unhandledRejection", (reason) =>
|
||||||
|
logger.error("Unhandled Rejection %O", reason),
|
||||||
|
);
|
||||||
|
|
||||||
app.listen(port).then(() => {
|
app.listen(port).then(() => {
|
||||||
logger.info(`Feathers app listening on http://${host}:${port}`)
|
logger.info(`Feathers app listening on http://${host}:${port}`);
|
||||||
})
|
});
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
// For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html
|
// For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html
|
||||||
import { createLogger, format, transports } from 'winston'
|
import { createLogger, format, transports } from "winston";
|
||||||
|
|
||||||
// Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston
|
// Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston
|
||||||
export const logger = createLogger({
|
export const logger = createLogger({
|
||||||
// To see more detailed errors, change this to 'debug'
|
// To see more detailed errors, change this to 'debug'
|
||||||
level: 'info',
|
level: "info",
|
||||||
format: format.combine(format.splat(), format.simple()),
|
format: format.combine(format.splat(), format.simple()),
|
||||||
transports: [new transports.Console()]
|
transports: [new transports.Console()],
|
||||||
})
|
});
|
||||||
|
88
src/app.ts
88
src/app.ts
@ -1,4 +1,4 @@
|
|||||||
import { feathers } from '@feathersjs/feathers'
|
import { feathers } from "@feathersjs/feathers";
|
||||||
import express, {
|
import express, {
|
||||||
rest,
|
rest,
|
||||||
json,
|
json,
|
||||||
@ -6,80 +6,84 @@ import express, {
|
|||||||
cors,
|
cors,
|
||||||
serveStatic,
|
serveStatic,
|
||||||
notFound,
|
notFound,
|
||||||
errorHandler
|
errorHandler,
|
||||||
} from '@feathersjs/express'
|
} from "@feathersjs/express";
|
||||||
import configuration from '@feathersjs/configuration'
|
import configuration from "@feathersjs/configuration";
|
||||||
import socketio from '@feathersjs/socketio'
|
import socketio from "@feathersjs/socketio";
|
||||||
import session from 'express-session';
|
import session from "express-session";
|
||||||
import cookieParser from 'cookie-parser';
|
import cookieParser from "cookie-parser";
|
||||||
|
|
||||||
import type { Application } from './declarations'
|
import type { Application } from "./declarations";
|
||||||
|
|
||||||
import { logger } from './logger'
|
import { logger } from "./logger";
|
||||||
import { logError } from './hooks/log-error'
|
import { logError } from "./hooks/log-error";
|
||||||
import { services } from './services/index'
|
import { services } from "./services/index";
|
||||||
import { channels } from './channels'
|
import { channels } from "./channels";
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from "crypto";
|
||||||
|
|
||||||
const app: Application = express(feathers())
|
const app: Application = express(feathers());
|
||||||
|
|
||||||
// Load app configuration
|
// Load app configuration
|
||||||
app.configure(configuration())
|
app.configure(configuration());
|
||||||
app.use(cors())
|
app.use(cors());
|
||||||
app.use(json({
|
app.use(
|
||||||
limit: '20mb'
|
json({
|
||||||
}))
|
limit: "20mb",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
app.use(session({
|
app.use(
|
||||||
|
session({
|
||||||
secret: randomUUID(),
|
secret: randomUUID(),
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: true,
|
saveUninitialized: true,
|
||||||
cookie: { secure: false }
|
cookie: { secure: false },
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
// Propagate session to request.params in feathers services
|
// Propagate session to request.params in feathers services
|
||||||
app.use(function (req, _res, next) {
|
app.use(function (req, _res, next) {
|
||||||
req.feathers = {
|
req.feathers = {
|
||||||
...req.feathers,
|
...req.feathers,
|
||||||
session: req.session
|
session: req.session,
|
||||||
}
|
};
|
||||||
next()
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(urlencoded({ extended: true }))
|
app.use(urlencoded({ extended: true }));
|
||||||
// Host the public folder
|
// Host the public folder
|
||||||
app.use('/', serveStatic(app.get('public')))
|
app.use("/", serveStatic(app.get("public")));
|
||||||
|
|
||||||
// Configure services and real-time functionality
|
// Configure services and real-time functionality
|
||||||
app.configure(rest())
|
app.configure(rest());
|
||||||
app.configure(
|
app.configure(
|
||||||
socketio({
|
socketio({
|
||||||
cors: {
|
cors: {
|
||||||
origin: app.get('origins')
|
origin: app.get("origins"),
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
)
|
);
|
||||||
app.configure(services)
|
app.configure(services);
|
||||||
app.configure(channels)
|
app.configure(channels);
|
||||||
|
|
||||||
// Configure a middleware for 404s and the error handler
|
// Configure a middleware for 404s and the error handler
|
||||||
app.use(notFound())
|
app.use(notFound());
|
||||||
app.use(errorHandler({ logger }))
|
app.use(errorHandler({ logger }));
|
||||||
|
|
||||||
// Register hooks that run on all service methods
|
// Register hooks that run on all service methods
|
||||||
app.hooks({
|
app.hooks({
|
||||||
around: {
|
around: {
|
||||||
all: [logError]
|
all: [logError],
|
||||||
},
|
},
|
||||||
before: {},
|
before: {},
|
||||||
after: {},
|
after: {},
|
||||||
error: {}
|
error: {},
|
||||||
})
|
});
|
||||||
// Register application setup and teardown hooks here
|
// Register application setup and teardown hooks here
|
||||||
app.hooks({
|
app.hooks({
|
||||||
setup: [],
|
setup: [],
|
||||||
teardown: []
|
teardown: [],
|
||||||
})
|
});
|
||||||
|
|
||||||
export { app }
|
export { app };
|
||||||
|
@ -1,31 +1,34 @@
|
|||||||
// For more information about this file see https://dove.feathersjs.com/guides/cli/channels.html
|
// For more information about this file see https://dove.feathersjs.com/guides/cli/channels.html
|
||||||
import type { RealTimeConnection, Params } from '@feathersjs/feathers'
|
import type { RealTimeConnection, Params } from "@feathersjs/feathers";
|
||||||
import type { AuthenticationResult } from '@feathersjs/authentication'
|
import type { AuthenticationResult } from "@feathersjs/authentication";
|
||||||
import '@feathersjs/transport-commons'
|
import "@feathersjs/transport-commons";
|
||||||
import type { Application, HookContext } from './declarations'
|
import type { Application, HookContext } from "./declarations";
|
||||||
import { logger } from './logger'
|
import { logger } from "./logger";
|
||||||
|
|
||||||
export const channels = (app: Application) => {
|
export const channels = (app: Application) => {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
'Publishing all events to all authenticated users. See `channels.ts` and https://dove.feathersjs.com/api/channels.html for more information.'
|
"Publishing all events to all authenticated users. See `channels.ts` and https://dove.feathersjs.com/api/channels.html for more information.",
|
||||||
)
|
);
|
||||||
|
|
||||||
app.on('connection', (connection: RealTimeConnection) => {
|
app.on("connection", (connection: RealTimeConnection) => {
|
||||||
// On a new real-time connection, add it to the anonymous channel
|
// On a new real-time connection, add it to the anonymous channel
|
||||||
app.channel('anonymous').join(connection)
|
app.channel("anonymous").join(connection);
|
||||||
})
|
});
|
||||||
|
|
||||||
app.on('login', (authResult: AuthenticationResult, { connection }: Params) => {
|
app.on(
|
||||||
|
"login",
|
||||||
|
(authResult: AuthenticationResult, { connection }: Params) => {
|
||||||
// connection can be undefined if there is no
|
// connection can be undefined if there is no
|
||||||
// real-time connection, e.g. when logging in via REST
|
// real-time connection, e.g. when logging in via REST
|
||||||
if (connection) {
|
if (connection) {
|
||||||
// The connection is no longer anonymous, remove it
|
// The connection is no longer anonymous, remove it
|
||||||
app.channel('anonymous').leave(connection)
|
app.channel("anonymous").leave(connection);
|
||||||
|
|
||||||
// Add it to the authenticated user channel
|
// Add it to the authenticated user channel
|
||||||
app.channel('authenticated').join(connection)
|
app.channel("authenticated").join(connection);
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
app.publish((data: any, context: HookContext) => {
|
app.publish((data: any, context: HookContext) => {
|
||||||
@ -33,6 +36,6 @@ export const channels = (app: Application) => {
|
|||||||
// To publish only for a specific event use `app.publish(eventname, () => {})`
|
// To publish only for a specific event use `app.publish(eventname, () => {})`
|
||||||
|
|
||||||
// e.g. to publish all service events to all authenticated users use
|
// e.g. to publish all service events to all authenticated users use
|
||||||
return app.channel('authenticated')
|
return app.channel("authenticated");
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
import config from 'config';
|
import config from "config";
|
||||||
|
|
||||||
const wildDuckClient = axios.create({
|
const wildDuckClient = axios.create({
|
||||||
baseURL: config.get('wildDuck.url'),
|
baseURL: config.get("wildDuck.url"),
|
||||||
headers: {
|
headers: {
|
||||||
'X-Access-Token': config.get('wildDuck.token'),
|
"X-Access-Token": config.get("wildDuck.token"),
|
||||||
},
|
},
|
||||||
responseType: 'json',
|
responseType: "json",
|
||||||
});
|
});
|
||||||
|
|
||||||
export default wildDuckClient;
|
export default wildDuckClient;
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
// For more information about this file see https://dove.feathersjs.com/guides/cli/typescript.html
|
// For more information about this file see https://dove.feathersjs.com/guides/cli/typescript.html
|
||||||
import { HookContext as FeathersHookContext, NextFunction } from '@feathersjs/feathers'
|
import {
|
||||||
import { Application as FeathersApplication } from '@feathersjs/express'
|
HookContext as FeathersHookContext,
|
||||||
type ApplicationConfiguration = any
|
NextFunction,
|
||||||
|
} from "@feathersjs/feathers";
|
||||||
|
import { Application as FeathersApplication } from "@feathersjs/express";
|
||||||
|
type ApplicationConfiguration = any;
|
||||||
|
|
||||||
export { NextFunction }
|
export { NextFunction };
|
||||||
|
|
||||||
// The types for app.get(name) and app.set(name)
|
// The types for app.get(name) and app.set(name)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
@ -14,7 +17,7 @@ export interface Configuration extends ApplicationConfiguration {}
|
|||||||
export interface ServiceTypes {}
|
export interface ServiceTypes {}
|
||||||
|
|
||||||
// The application instance type that will be used everywhere else
|
// The application instance type that will be used everywhere else
|
||||||
export type Application = FeathersApplication<ServiceTypes, Configuration>
|
export type Application = FeathersApplication<ServiceTypes, Configuration>;
|
||||||
|
|
||||||
// The context for hook functions - can be typed with a service class
|
// The context for hook functions - can be typed with a service class
|
||||||
export type HookContext<S = any> = FeathersHookContext<Application, S>
|
export type HookContext<S = any> = FeathersHookContext<Application, S>;
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import type { HookContext, NextFunction } from '../declarations'
|
import type { HookContext, NextFunction } from "../declarations";
|
||||||
import { logger } from '../logger'
|
import { logger } from "../logger";
|
||||||
|
|
||||||
export const logError = async (context: HookContext, next: NextFunction) => {
|
export const logError = async (context: HookContext, next: NextFunction) => {
|
||||||
try {
|
try {
|
||||||
await next()
|
await next();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
logger.error(error.stack)
|
logger.error(error.stack);
|
||||||
|
|
||||||
// Log validation errors
|
// Log validation errors
|
||||||
if (error.data) {
|
if (error.data) {
|
||||||
logger.error('Data: %O', error.data)
|
logger.error("Data: %O", error.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { NotAuthenticated } from '@feathersjs/errors'
|
import { NotAuthenticated } from "@feathersjs/errors";
|
||||||
import type { HookContext, NextFunction } from '../declarations'
|
import type { HookContext, NextFunction } from "../declarations";
|
||||||
|
|
||||||
// Check if user is stored in session
|
// Check if user is stored in session
|
||||||
export const validateAuth = async (context: HookContext) => {
|
export const validateAuth = async (context: HookContext) => {
|
||||||
if (!context.params.session?.user) {
|
if (!context.params.session?.user) {
|
||||||
throw new NotAuthenticated('Not authenticated')
|
throw new NotAuthenticated("Not authenticated");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
16
src/index.ts
16
src/index.ts
@ -1,11 +1,13 @@
|
|||||||
import { app } from './app'
|
import { app } from "./app";
|
||||||
import { logger } from './logger'
|
import { logger } from "./logger";
|
||||||
|
|
||||||
const port = app.get('port')
|
const port = app.get("port");
|
||||||
const host = app.get('host')
|
const host = app.get("host");
|
||||||
|
|
||||||
process.on('unhandledRejection', (reason) => logger.error('Unhandled Rejection %O', reason))
|
process.on("unhandledRejection", (reason) =>
|
||||||
|
logger.error("Unhandled Rejection %O", reason),
|
||||||
|
);
|
||||||
|
|
||||||
app.listen(port).then(() => {
|
app.listen(port).then(() => {
|
||||||
logger.info(`Feathers app listening on http://${host}:${port}`)
|
logger.info(`Feathers app listening on http://${host}:${port}`);
|
||||||
})
|
});
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
// For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html
|
// For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html
|
||||||
import { createLogger, format, transports } from 'winston'
|
import { createLogger, format, transports } from "winston";
|
||||||
|
|
||||||
// Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston
|
// Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston
|
||||||
export const logger = createLogger({
|
export const logger = createLogger({
|
||||||
// To see more detailed errors, change this to 'debug'
|
// To see more detailed errors, change this to 'debug'
|
||||||
level: 'info',
|
level: "info",
|
||||||
format: format.combine(format.splat(), format.simple()),
|
format: format.combine(format.splat(), format.simple()),
|
||||||
transports: [new transports.Console()]
|
transports: [new transports.Console()],
|
||||||
})
|
});
|
||||||
|
@ -1,92 +1,123 @@
|
|||||||
import type { Params, ServiceInterface } from '@feathersjs/feathers'
|
import type {
|
||||||
|
NullableId,
|
||||||
|
Params,
|
||||||
|
ServiceInterface,
|
||||||
|
} from "@feathersjs/feathers";
|
||||||
|
|
||||||
import type { Application } from '../../declarations'
|
import type { Application } from "../../declarations";
|
||||||
import wildDuckClient from '../../clients/wildduck.client'
|
import wildDuckClient from "../../clients/wildduck.client";
|
||||||
import { faker } from '@faker-js/faker'
|
import { faker } from "@faker-js/faker";
|
||||||
import { BadRequest } from '@feathersjs/errors'
|
import { BadRequest } from "@feathersjs/errors";
|
||||||
import config from 'config'
|
import config from "config";
|
||||||
|
|
||||||
interface Alias {
|
interface Alias {
|
||||||
success: boolean,
|
success: boolean;
|
||||||
id: string,
|
id: string;
|
||||||
address: string,
|
address: string;
|
||||||
main: boolean,
|
main: boolean;
|
||||||
user: string,
|
user: string;
|
||||||
tags: string[],
|
tags: string[];
|
||||||
created: string,
|
created: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetAddressInfoResponse {
|
interface GetAddressInfoResponse {
|
||||||
success: boolean,
|
success: boolean;
|
||||||
results: Alias[]
|
results: Alias[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateAddressResponse {
|
interface CreateAddressResponse {
|
||||||
success: boolean,
|
success: boolean;
|
||||||
id: string,
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type AliasesData = any
|
type AliasesData = any;
|
||||||
type AliasesPatch = any
|
type AliasesPatch = any;
|
||||||
type AliasesQuery = any
|
type AliasesQuery = any;
|
||||||
|
|
||||||
export type { Alias as Aliases, AliasesData, AliasesPatch, AliasesQuery }
|
export type { Alias as Aliases, AliasesData, AliasesPatch, AliasesQuery };
|
||||||
|
|
||||||
export interface AliasesServiceOptions {
|
export interface AliasesServiceOptions {
|
||||||
app: Application
|
app: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AliasesParams extends Params<AliasesQuery> {
|
export interface AliasesParams extends Params<AliasesQuery> {
|
||||||
session?: any
|
session?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
||||||
implements ServiceInterface<Alias, AliasesData, ServiceParams, AliasesPatch>
|
implements ServiceInterface<Alias, AliasesData, ServiceParams, AliasesPatch>
|
||||||
{
|
{
|
||||||
constructor(public options: AliasesServiceOptions) { }
|
constructor(public options: AliasesServiceOptions) {}
|
||||||
|
|
||||||
async find(params: ServiceParams): Promise<Alias[]> {
|
async find(params: ServiceParams): Promise<Alias[]> {
|
||||||
const userId = await this.getUserIdByEmailAddress(params)
|
const userId = await this.getUserIdByEmailAddress(params);
|
||||||
const { data: userAddressesResponse } = await wildDuckClient.get<GetAddressInfoResponse>(`/users/${userId}/addresses`)
|
|
||||||
|
|
||||||
return userAddressesResponse.results
|
return this.getUserAddresses(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: AliasesData, params: ServiceParams): Promise<Alias>
|
async create(data: AliasesData, params: ServiceParams): Promise<Alias>;
|
||||||
async create(data: AliasesData, params: ServiceParams): Promise<Alias | Alias[]> {
|
async create(
|
||||||
const userId = await this.getUserIdByEmailAddress(params)
|
data: AliasesData,
|
||||||
const aliasFirstPart = faker.animal.crocodilia()
|
params: ServiceParams,
|
||||||
.replace(/\D/g, '')
|
): Promise<Alias | Alias[]> {
|
||||||
.replace(/\s/g, '')
|
const userId = await this.getUserIdByEmailAddress(params);
|
||||||
|
const aliasFirstPart = faker.animal
|
||||||
|
.crocodilia()
|
||||||
|
.replace(/\D/g, "")
|
||||||
|
.replace(/\s/g, "")
|
||||||
.slice(0, 10);
|
.slice(0, 10);
|
||||||
|
|
||||||
const aliasSecondPart = faker.git.commitSha({ length: 5 });
|
const aliasSecondPart = faker.git.commitSha({ length: 5 });
|
||||||
const alias = `${aliasFirstPart}-${aliasSecondPart}@${config.get('wildDuck.domain')}`;
|
const alias = `${aliasFirstPart}-${aliasSecondPart}@${config.get(
|
||||||
// const alias = `${faker.animal.crocodilia().replace(/\s/, '').slice(10)}-${faker.git.commitSha({ length: 5 })}`;
|
"wildDuck.domain",
|
||||||
|
)}`;
|
||||||
|
|
||||||
const createResult = await wildDuckClient.post<CreateAddressResponse>(`/users/${userId}/addresses`, {
|
const createResult = await wildDuckClient.post<CreateAddressResponse>(
|
||||||
address: alias
|
`/users/${userId}/addresses`,
|
||||||
})
|
{
|
||||||
|
address: alias,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (!createResult.data.success) {
|
if (!createResult.data.success) {
|
||||||
throw new BadRequest('Failed to create alias')
|
throw new BadRequest("Failed to create alias");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data: userAddressesResponse } = await wildDuckClient.get<GetAddressInfoResponse>(`/users/${userId}/addresses`)
|
return this.getUserAddresses(userId);
|
||||||
|
|
||||||
return userAddressesResponse.results
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getUserIdByEmailAddress(params: ServiceParams): Promise<string> {
|
private async getUserIdByEmailAddress(
|
||||||
|
params: ServiceParams,
|
||||||
|
): Promise<string> {
|
||||||
const emails = params.session?.user?.emails;
|
const emails = params.session?.user?.emails;
|
||||||
|
|
||||||
const addressInfoResponse = await Promise.any(emails.map((email: string) => wildDuckClient.get<Alias>(`addresses/resolve/${email}`)))
|
const addressInfoResponse = await Promise.any(
|
||||||
|
emails.map((email: string) =>
|
||||||
|
wildDuckClient.get<Alias>(`addresses/resolve/${email}`),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return addressInfoResponse.data.user
|
return addressInfoResponse.data.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getUserAddresses(userId: string): Promise<Alias[]> {
|
||||||
|
const { data: userAddressesResponse } =
|
||||||
|
await wildDuckClient.get<GetAddressInfoResponse>(
|
||||||
|
`/users/${userId}/addresses`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return userAddressesResponse.results;
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: NullableId, params: ServiceParams): Promise<Alias[]> {
|
||||||
|
await wildDuckClient.delete<Alias>(`/addresses/${id}`);
|
||||||
|
|
||||||
|
const userId = await this.getUserIdByEmailAddress(params);
|
||||||
|
|
||||||
|
return this.getUserAddresses(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getOptions = (app: Application) => {
|
export const getOptions = (app: Application) => {
|
||||||
return { app }
|
return { app };
|
||||||
}
|
};
|
||||||
|
@ -1,41 +1,39 @@
|
|||||||
import type { Application } from '../../declarations'
|
import type { Application } from "../../declarations";
|
||||||
import { validateAuth } from '../../hooks/validate-auth'
|
import { validateAuth } from "../../hooks/validate-auth";
|
||||||
import { AliasesService, getOptions } from './aliases.class'
|
import { AliasesService, getOptions } from "./aliases.class";
|
||||||
|
|
||||||
export const aliasesPath = 'aliases'
|
export const aliasesPath = "aliases";
|
||||||
export const aliasesMethods = ['find', 'create'] as const
|
export const aliasesMethods = ["find", "create"] as const;
|
||||||
|
|
||||||
export * from './aliases.class'
|
export * from "./aliases.class";
|
||||||
|
|
||||||
export const aliases = (app: Application) => {
|
export const aliases = (app: Application) => {
|
||||||
app.use(aliasesPath, new AliasesService(getOptions(app)), {
|
app.use(aliasesPath, new AliasesService(getOptions(app)), {
|
||||||
methods: aliasesMethods,
|
methods: aliasesMethods,
|
||||||
events: []
|
events: [],
|
||||||
})
|
});
|
||||||
|
|
||||||
app.service(aliasesPath).hooks({
|
app.service(aliasesPath).hooks({
|
||||||
around: {
|
around: {
|
||||||
all: []
|
all: [],
|
||||||
},
|
},
|
||||||
before: {
|
before: {
|
||||||
all: [
|
all: [validateAuth],
|
||||||
validateAuth
|
|
||||||
],
|
|
||||||
find: [],
|
find: [],
|
||||||
create: [],
|
create: [],
|
||||||
},
|
},
|
||||||
after: {
|
after: {
|
||||||
all: []
|
all: [],
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
all: []
|
all: [],
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
// Add this service to the service type index
|
// Add this service to the service type index
|
||||||
declare module '../../declarations' {
|
declare module "../../declarations" {
|
||||||
interface ServiceTypes {
|
interface ServiceTypes {
|
||||||
[aliasesPath]: AliasesService
|
[aliasesPath]: AliasesService;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,46 +1,46 @@
|
|||||||
import type { Params, ServiceInterface } from '@feathersjs/feathers'
|
import type { Params, ServiceInterface } from "@feathersjs/feathers";
|
||||||
|
|
||||||
import type { Application } from '../../declarations'
|
import type { Application } from "../../declarations";
|
||||||
|
|
||||||
import { Issuer, generators } from 'openid-client'
|
import { Issuer, generators } from "openid-client";
|
||||||
import config from 'config';
|
import config from "config";
|
||||||
|
|
||||||
|
type AuthOidcResponse = string;
|
||||||
|
type AuthOidcQuery = any;
|
||||||
|
|
||||||
type AuthOidcResponse = string
|
export type { AuthOidcResponse as AuthOidc, AuthOidcQuery };
|
||||||
type AuthOidcQuery = any
|
|
||||||
|
|
||||||
export type { AuthOidcResponse as AuthOidc, AuthOidcQuery }
|
|
||||||
|
|
||||||
export interface AuthOidcServiceOptions {
|
export interface AuthOidcServiceOptions {
|
||||||
app: Application
|
app: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthOidcParams extends Params<AuthOidcQuery> {
|
export interface AuthOidcParams extends Params<AuthOidcQuery> {
|
||||||
session?: any
|
session?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AuthOidcService<ServiceParams extends AuthOidcParams = AuthOidcParams>
|
export class AuthOidcService<
|
||||||
implements ServiceInterface<AuthOidcResponse, ServiceParams>
|
ServiceParams extends AuthOidcParams = AuthOidcParams,
|
||||||
|
> implements ServiceInterface<AuthOidcResponse, ServiceParams>
|
||||||
{
|
{
|
||||||
constructor(public options: AuthOidcServiceOptions) { }
|
constructor(public options: AuthOidcServiceOptions) {}
|
||||||
|
|
||||||
async find(params: ServiceParams): Promise<AuthOidcResponse> {
|
async find(params: ServiceParams): Promise<AuthOidcResponse> {
|
||||||
const issuer = await Issuer.discover(config.get('oidc.gatewayUri'));
|
const issuer = await Issuer.discover(config.get("oidc.gatewayUri"));
|
||||||
const client = new issuer.Client({
|
const client = new issuer.Client({
|
||||||
client_id: config.get('oidc.clientId'),
|
client_id: config.get("oidc.clientId"),
|
||||||
client_secret: config.get('oidc.clientSecret'),
|
client_secret: config.get("oidc.clientSecret"),
|
||||||
redirect_uris: [config.get('oidc.redirectUris')],
|
redirect_uris: [config.get("oidc.redirectUris")],
|
||||||
response_types: ['code'],
|
response_types: ["code"],
|
||||||
})
|
});
|
||||||
const codeVerifier = generators.codeVerifier();
|
const codeVerifier = generators.codeVerifier();
|
||||||
const codeChallenge = generators.codeChallenge(codeVerifier);
|
const codeChallenge = generators.codeChallenge(codeVerifier);
|
||||||
|
|
||||||
const url = client.authorizationUrl({
|
const url = client.authorizationUrl({
|
||||||
redirect_uri: config.get('clientUrl') + '/auth-oidc/callback',
|
redirect_uri: config.get("clientUrl") + "/auth-oidc/callback",
|
||||||
scope: 'openid profile offline_access',
|
scope: "openid profile offline_access",
|
||||||
response_type: 'code',
|
response_type: "code",
|
||||||
code_challenge: codeChallenge,
|
code_challenge: codeChallenge,
|
||||||
code_challenge_method: 'S256',
|
code_challenge_method: "S256",
|
||||||
});
|
});
|
||||||
|
|
||||||
params.session.codeVerifier = codeVerifier;
|
params.session.codeVerifier = codeVerifier;
|
||||||
@ -49,5 +49,5 @@ export class AuthOidcService<ServiceParams extends AuthOidcParams = AuthOidcPara
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getOptions = (app: Application) => {
|
export const getOptions = (app: Application) => {
|
||||||
return { app }
|
return { app };
|
||||||
}
|
};
|
||||||
|
@ -1,41 +1,45 @@
|
|||||||
import type { Application } from '../../declarations'
|
import type { Application } from "../../declarations";
|
||||||
import { AuthOidcService, getOptions } from './auth-oidc.class'
|
import { AuthOidcService, getOptions } from "./auth-oidc.class";
|
||||||
|
|
||||||
export const authOidcPath = 'auth-oidc'
|
export const authOidcPath = "auth-oidc";
|
||||||
export const authOidcMethods = ['find'] as const
|
export const authOidcMethods = ["find"] as const;
|
||||||
|
|
||||||
export * from './auth-oidc.class'
|
export * from "./auth-oidc.class";
|
||||||
|
|
||||||
export const authOidc = (app: Application) => {
|
export const authOidc = (app: Application) => {
|
||||||
// TODO: fix this to use the correct type
|
// TODO: fix this to use the correct type
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
app.use(authOidcPath, new AuthOidcService(getOptions(app)), {
|
app.use(
|
||||||
|
authOidcPath,
|
||||||
|
new AuthOidcService(getOptions(app)),
|
||||||
|
{
|
||||||
methods: authOidcMethods,
|
methods: authOidcMethods,
|
||||||
events: []
|
events: [],
|
||||||
}, (req: any, res: any) => {
|
},
|
||||||
|
(req: any, res: any) => {
|
||||||
return res.redirect(res.data);
|
return res.redirect(res.data);
|
||||||
})
|
},
|
||||||
|
);
|
||||||
|
|
||||||
app.service(authOidcPath).hooks({
|
app.service(authOidcPath).hooks({
|
||||||
around: {
|
around: {
|
||||||
all: []
|
all: [],
|
||||||
},
|
},
|
||||||
before: {
|
before: {
|
||||||
all: [],
|
all: [],
|
||||||
find: [],
|
find: [],
|
||||||
},
|
},
|
||||||
after: {
|
after: {
|
||||||
all: []
|
all: [],
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
all: []
|
all: [],
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
declare module '../../declarations' {
|
declare module "../../declarations" {
|
||||||
interface ServiceTypes {
|
interface ServiceTypes {
|
||||||
[authOidcPath]: AuthOidcService
|
[authOidcPath]: AuthOidcService;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,68 @@
|
|||||||
import type { Params, ServiceInterface } from '@feathersjs/feathers'
|
import type { Params, ServiceInterface } from "@feathersjs/feathers";
|
||||||
import type { Application } from '../../../declarations'
|
import type { Application } from "../../../declarations";
|
||||||
import { Issuer } from 'openid-client'
|
import { Issuer } from "openid-client";
|
||||||
|
|
||||||
import config from 'config'
|
import config from "config";
|
||||||
|
|
||||||
type AuthOidcCallback = string
|
type AuthOidcCallback = string;
|
||||||
type AuthOidcCallbackData = any
|
type AuthOidcCallbackData = any;
|
||||||
type AuthOidcCallbackPatch = any
|
type AuthOidcCallbackPatch = any;
|
||||||
type AuthOidcCallbackQuery = any
|
type AuthOidcCallbackQuery = any;
|
||||||
|
|
||||||
export type { AuthOidcCallback, AuthOidcCallbackData, AuthOidcCallbackPatch, AuthOidcCallbackQuery }
|
export type {
|
||||||
|
AuthOidcCallback,
|
||||||
|
AuthOidcCallbackData,
|
||||||
|
AuthOidcCallbackPatch,
|
||||||
|
AuthOidcCallbackQuery,
|
||||||
|
};
|
||||||
|
|
||||||
export interface AuthOidcCallbackServiceOptions {
|
export interface AuthOidcCallbackServiceOptions {
|
||||||
app: Application
|
app: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthOidcCallbackParams extends Params<AuthOidcCallbackQuery> {
|
export interface AuthOidcCallbackParams extends Params<AuthOidcCallbackQuery> {
|
||||||
session?: any
|
session?: any;
|
||||||
query: {
|
query: {
|
||||||
iss: string,
|
iss: string;
|
||||||
code: string,
|
code: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AuthOidcCallbackService<ServiceParams extends AuthOidcCallbackParams = AuthOidcCallbackParams>
|
export class AuthOidcCallbackService<
|
||||||
implements ServiceInterface<AuthOidcCallback, AuthOidcCallbackData, ServiceParams, AuthOidcCallbackPatch>
|
ServiceParams extends AuthOidcCallbackParams = AuthOidcCallbackParams,
|
||||||
|
> implements
|
||||||
|
ServiceInterface<
|
||||||
|
AuthOidcCallback,
|
||||||
|
AuthOidcCallbackData,
|
||||||
|
ServiceParams,
|
||||||
|
AuthOidcCallbackPatch
|
||||||
|
>
|
||||||
{
|
{
|
||||||
constructor(public options: AuthOidcCallbackServiceOptions) { }
|
constructor(public options: AuthOidcCallbackServiceOptions) {}
|
||||||
|
|
||||||
async find(params: ServiceParams): Promise<AuthOidcCallback> {
|
async find(params: ServiceParams): Promise<AuthOidcCallback> {
|
||||||
const issuer = await Issuer.discover(config.get('oidc.gatewayUri'));
|
const issuer = await Issuer.discover(config.get("oidc.gatewayUri"));
|
||||||
const client = new issuer.Client({
|
const client = new issuer.Client({
|
||||||
client_id: config.get('oidc.clientId'),
|
client_id: config.get("oidc.clientId"),
|
||||||
client_secret: config.get('oidc.clientSecret'),
|
client_secret: config.get("oidc.clientSecret"),
|
||||||
redirect_uris: [config.get('oidc.redirectUris')],
|
redirect_uris: [config.get("oidc.redirectUris")],
|
||||||
response_types: ['code'],
|
response_types: ["code"],
|
||||||
})
|
});
|
||||||
|
|
||||||
const codeVerifier = params.session.codeVerifier;
|
const codeVerifier = params.session.codeVerifier;
|
||||||
const tokenSet = await client.callback(config.get('clientUrl') + '/auth-oidc/callback', { code: params.query.code, iss: params.query.iss }, { code_verifier: codeVerifier });
|
const tokenSet = await client.callback(
|
||||||
|
config.get("clientUrl") + "/auth-oidc/callback",
|
||||||
|
{ code: params.query.code, iss: params.query.iss },
|
||||||
|
{ code_verifier: codeVerifier },
|
||||||
|
);
|
||||||
const userinfo = await client.userinfo(tokenSet.access_token as string);
|
const userinfo = await client.userinfo(tokenSet.access_token as string);
|
||||||
|
|
||||||
params.session.user = userinfo;
|
params.session.user = userinfo;
|
||||||
|
|
||||||
return '/'
|
return "/";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getOptions = (app: Application) => {
|
export const getOptions = (app: Application) => {
|
||||||
return { app }
|
return { app };
|
||||||
}
|
};
|
||||||
|
@ -1,42 +1,49 @@
|
|||||||
import { http } from '@feathersjs/transport-commons'
|
import { http } from "@feathersjs/transport-commons";
|
||||||
import type { Application } from '../../../declarations'
|
import type { Application } from "../../../declarations";
|
||||||
import { AuthOidcCallbackService, getOptions } from './auth-oidc-callback.class'
|
import {
|
||||||
|
AuthOidcCallbackService,
|
||||||
|
getOptions,
|
||||||
|
} from "./auth-oidc-callback.class";
|
||||||
|
|
||||||
export const authOidcCallbackPath = 'auth-oidc/callback'
|
export const authOidcCallbackPath = "auth-oidc/callback";
|
||||||
export const authOidcCallbackMethods = ['find'] as const
|
export const authOidcCallbackMethods = ["find"] as const;
|
||||||
|
|
||||||
export * from './auth-oidc-callback.class'
|
export * from "./auth-oidc-callback.class";
|
||||||
|
|
||||||
export const authOidcCallback = (app: Application) => {
|
export const authOidcCallback = (app: Application) => {
|
||||||
// TODO: fix this to use the correct type
|
// TODO: fix this to use the correct type
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
app.use(authOidcCallbackPath, new AuthOidcCallbackService(getOptions(app)), {
|
app.use(
|
||||||
|
authOidcCallbackPath,
|
||||||
|
new AuthOidcCallbackService(getOptions(app)),
|
||||||
|
{
|
||||||
methods: authOidcCallbackMethods,
|
methods: authOidcCallbackMethods,
|
||||||
events: []
|
events: [],
|
||||||
}, (req: any, res: any) => {
|
},
|
||||||
|
(req: any, res: any) => {
|
||||||
return res.redirect(res.data);
|
return res.redirect(res.data);
|
||||||
})
|
},
|
||||||
|
);
|
||||||
|
|
||||||
app.service(authOidcCallbackPath).hooks({
|
app.service(authOidcCallbackPath).hooks({
|
||||||
around: {
|
around: {
|
||||||
all: []
|
all: [],
|
||||||
},
|
},
|
||||||
before: {
|
before: {
|
||||||
all: [],
|
all: [],
|
||||||
find: [],
|
find: [],
|
||||||
},
|
},
|
||||||
after: {
|
after: {
|
||||||
all: []
|
all: [],
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
all: []
|
all: [],
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
declare module '../../../declarations' {
|
declare module "../../../declarations" {
|
||||||
interface ServiceTypes {
|
interface ServiceTypes {
|
||||||
[authOidcCallbackPath]: AuthOidcCallbackService
|
[authOidcCallbackPath]: AuthOidcCallbackService;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { authOidcCallback } from './auth-oidc/callback/auth-oidc-callback'
|
import { authOidcCallback } from "./auth-oidc/callback/auth-oidc-callback";
|
||||||
import { authOidc } from './auth-oidc/auth-oidc'
|
import { authOidc } from "./auth-oidc/auth-oidc";
|
||||||
import { aliases } from './aliases/aliases'
|
import { aliases } from "./aliases/aliases";
|
||||||
import type { Application } from '../declarations'
|
import type { Application } from "../declarations";
|
||||||
|
|
||||||
export const services = (app: Application) => {
|
export const services = (app: Application) => {
|
||||||
app.configure(authOidcCallback)
|
app.configure(authOidcCallback);
|
||||||
app.configure(authOidc)
|
app.configure(authOidc);
|
||||||
app.configure(aliases)
|
app.configure(aliases);
|
||||||
}
|
};
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
// For more information about this file see https://dove.feathersjs.com/guides/cli/validators.html
|
// For more information about this file see https://dove.feathersjs.com/guides/cli/validators.html
|
||||||
import { Ajv, addFormats } from '@feathersjs/schema'
|
import { Ajv, addFormats } from "@feathersjs/schema";
|
||||||
import type { FormatsPluginOptions } from '@feathersjs/schema'
|
import type { FormatsPluginOptions } from "@feathersjs/schema";
|
||||||
|
|
||||||
const formats: FormatsPluginOptions = [
|
const formats: FormatsPluginOptions = [
|
||||||
'date-time',
|
"date-time",
|
||||||
'time',
|
"time",
|
||||||
'date',
|
"date",
|
||||||
'email',
|
"email",
|
||||||
'hostname',
|
"hostname",
|
||||||
'ipv4',
|
"ipv4",
|
||||||
'ipv6',
|
"ipv6",
|
||||||
'uri',
|
"uri",
|
||||||
'uri-reference',
|
"uri-reference",
|
||||||
'uuid',
|
"uuid",
|
||||||
'uri-template',
|
"uri-template",
|
||||||
'json-pointer',
|
"json-pointer",
|
||||||
'relative-json-pointer',
|
"relative-json-pointer",
|
||||||
'regex'
|
"regex",
|
||||||
]
|
];
|
||||||
|
|
||||||
export const dataValidator: Ajv = addFormats(new Ajv({}), formats)
|
export const dataValidator: Ajv = addFormats(new Ajv({}), formats);
|
||||||
|
|
||||||
export const queryValidator: Ajv = addFormats(
|
export const queryValidator: Ajv = addFormats(
|
||||||
new Ajv({
|
new Ajv({
|
||||||
coerceTypes: true
|
coerceTypes: true,
|
||||||
}),
|
}),
|
||||||
formats
|
formats,
|
||||||
)
|
);
|
||||||
|
@ -1,40 +1,40 @@
|
|||||||
// For more information about this file see https://dove.feathersjs.com/guides/cli/app.test.html
|
// For more information about this file see https://dove.feathersjs.com/guides/cli/app.test.html
|
||||||
import assert from 'assert'
|
import assert from "assert";
|
||||||
import axios from 'axios'
|
import axios from "axios";
|
||||||
import type { Server } from 'http'
|
import type { Server } from "http";
|
||||||
import { app } from '../src/app'
|
import { app } from "../src/app";
|
||||||
|
|
||||||
const port = app.get('port')
|
const port = app.get("port");
|
||||||
const appUrl = `http://${app.get('host')}:${port}`
|
const appUrl = `http://${app.get("host")}:${port}`;
|
||||||
|
|
||||||
describe('Feathers application tests', () => {
|
describe("Feathers application tests", () => {
|
||||||
let server: Server
|
let server: Server;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
server = await app.listen(port)
|
server = await app.listen(port);
|
||||||
})
|
});
|
||||||
|
|
||||||
after(async () => {
|
after(async () => {
|
||||||
await app.teardown()
|
await app.teardown();
|
||||||
})
|
});
|
||||||
|
|
||||||
it('starts and shows the index page', async () => {
|
it("starts and shows the index page", async () => {
|
||||||
const { data } = await axios.get<string>(appUrl)
|
const { data } = await axios.get<string>(appUrl);
|
||||||
|
|
||||||
assert.ok(data.indexOf('<html lang="en">') !== -1)
|
assert.ok(data.indexOf('<html lang="en">') !== -1);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('shows a 404 JSON error', async () => {
|
it("shows a 404 JSON error", async () => {
|
||||||
try {
|
try {
|
||||||
await axios.get(`${appUrl}/path/to/nowhere`, {
|
await axios.get(`${appUrl}/path/to/nowhere`, {
|
||||||
responseType: 'json'
|
responseType: "json",
|
||||||
})
|
});
|
||||||
assert.fail('should never get here')
|
assert.fail("should never get here");
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const { response } = error
|
const { response } = error;
|
||||||
assert.strictEqual(response?.status, 404)
|
assert.strictEqual(response?.status, 404);
|
||||||
assert.strictEqual(response?.data?.code, 404)
|
assert.strictEqual(response?.data?.code, 404);
|
||||||
assert.strictEqual(response?.data?.name, 'NotFound')
|
assert.strictEqual(response?.data?.name, "NotFound");
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html
|
// For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html
|
||||||
import assert from 'assert'
|
import assert from "assert";
|
||||||
import { app } from '../../../src/app'
|
import { app } from "../../../src/app";
|
||||||
|
|
||||||
describe('aliases service', () => {
|
describe("aliases service", () => {
|
||||||
it('registered the service', () => {
|
it("registered the service", () => {
|
||||||
const service = app.service('aliases')
|
const service = app.service("aliases");
|
||||||
|
|
||||||
assert.ok(service, 'Registered the service')
|
assert.ok(service, "Registered the service");
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html
|
// For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html
|
||||||
import assert from 'assert'
|
import assert from "assert";
|
||||||
import { app } from '../../../src/app'
|
import { app } from "../../../src/app";
|
||||||
|
|
||||||
describe('auth-oidc service', () => {
|
describe("auth-oidc service", () => {
|
||||||
it('registered the service', () => {
|
it("registered the service", () => {
|
||||||
const service = app.service('auth-oidc')
|
const service = app.service("auth-oidc");
|
||||||
|
|
||||||
assert.ok(service, 'Registered the service')
|
assert.ok(service, "Registered the service");
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html
|
// For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html
|
||||||
import assert from 'assert'
|
import assert from "assert";
|
||||||
import { app } from '../../../../src/app'
|
import { app } from "../../../../src/app";
|
||||||
|
|
||||||
describe('auth-oidc/callback service', () => {
|
describe("auth-oidc/callback service", () => {
|
||||||
it('registered the service', () => {
|
it("registered the service", () => {
|
||||||
const service = app.service('auth-oidc/callback')
|
const service = app.service("auth-oidc/callback");
|
||||||
|
|
||||||
assert.ok(service, 'Registered the service')
|
assert.ok(service, "Registered the service");
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
// For more information about this file see https://dove.feathersjs.com/guides/cli/validators.html
|
// For more information about this file see https://dove.feathersjs.com/guides/cli/validators.html
|
||||||
import { Ajv, addFormats } from '@feathersjs/schema'
|
import { Ajv, addFormats } from "@feathersjs/schema";
|
||||||
import type { FormatsPluginOptions } from '@feathersjs/schema'
|
import type { FormatsPluginOptions } from "@feathersjs/schema";
|
||||||
|
|
||||||
const formats: FormatsPluginOptions = [
|
const formats: FormatsPluginOptions = [
|
||||||
'date-time',
|
"date-time",
|
||||||
'time',
|
"time",
|
||||||
'date',
|
"date",
|
||||||
'email',
|
"email",
|
||||||
'hostname',
|
"hostname",
|
||||||
'ipv4',
|
"ipv4",
|
||||||
'ipv6',
|
"ipv6",
|
||||||
'uri',
|
"uri",
|
||||||
'uri-reference',
|
"uri-reference",
|
||||||
'uuid',
|
"uuid",
|
||||||
'uri-template',
|
"uri-template",
|
||||||
'json-pointer',
|
"json-pointer",
|
||||||
'relative-json-pointer',
|
"relative-json-pointer",
|
||||||
'regex'
|
"regex",
|
||||||
]
|
];
|
||||||
|
|
||||||
export const dataValidator: Ajv = addFormats(new Ajv({}), formats)
|
export const dataValidator: Ajv = addFormats(new Ajv({}), formats);
|
||||||
|
|
||||||
export const queryValidator: Ajv = addFormats(
|
export const queryValidator: Ajv = addFormats(
|
||||||
new Ajv({
|
new Ajv({
|
||||||
coerceTypes: true
|
coerceTypes: true,
|
||||||
}),
|
}),
|
||||||
formats
|
formats,
|
||||||
)
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user