Skip to content

Commit

Permalink
🐛 fix: Fix git ignore
Browse files Browse the repository at this point in the history
  • Loading branch information
canisminor1990 committed Nov 7, 2023
1 parent 9ce0f91 commit 680649c
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 1 deletion.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ coverage
# production
dist
es
lib
logs
test-output

Expand Down
140 changes: 140 additions & 0 deletions lib/cors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/**
* Multi purpose CORS lib.
* Note: Based on the `cors` package in npm but using only
* web APIs. Feel free to use it in your own projects.
*/

type StaticOrigin = boolean | string | RegExp | (boolean | string | RegExp)[];

type OriginFn = (origin: string | undefined, req: Request) => StaticOrigin | Promise<StaticOrigin>;

interface CorsOptions {
allowedHeaders?: string | string[];
credentials?: boolean;
exposedHeaders?: string | string[];
maxAge?: number;
methods?: string | string[];
optionsSuccessStatus?: number;
origin?: StaticOrigin | OriginFn;
preflightContinue?: boolean;
}

const defaultOptions: CorsOptions = {
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
optionsSuccessStatus: 204,
origin: '*',
preflightContinue: false,
};

function isOriginAllowed(origin: string, allowed: StaticOrigin): boolean {
return Array.isArray(allowed)
? allowed.some((o) => isOriginAllowed(origin, o))
: typeof allowed === 'string'
? origin === allowed
: allowed instanceof RegExp
? allowed.test(origin)
: !!allowed;
}

function getOriginHeaders(reqOrigin: string | undefined, origin: StaticOrigin) {
const headers = new Headers();

if (origin === '*') {
// Allow any origin
headers.set('Access-Control-Allow-Origin', '*');
} else if (typeof origin === 'string') {
// Fixed origin
headers.set('Access-Control-Allow-Origin', origin);
headers.append('Vary', 'Origin');
} else {
const allowed = isOriginAllowed(reqOrigin ?? '', origin);

if (allowed && reqOrigin) {
headers.set('Access-Control-Allow-Origin', reqOrigin);
}
headers.append('Vary', 'Origin');
}

return headers;
}

// originHeadersFromReq

async function originHeadersFromReq(req: Request, origin: StaticOrigin | OriginFn) {
const reqOrigin = req.headers.get('Origin') || undefined;
const value = typeof origin === 'function' ? await origin(reqOrigin, req) : origin;

if (!value) return;
return getOriginHeaders(reqOrigin, value);
}

function getAllowedHeaders(req: Request, allowed?: string | string[]) {
const headers = new Headers();

if (!allowed) {
allowed = req.headers.get('Access-Control-Request-Headers')!;
headers.append('Vary', 'Access-Control-Request-Headers');
} else if (Array.isArray(allowed)) {
// If the allowed headers is an array, turn it into a string
allowed = allowed.join(',');
}
if (allowed) {
headers.set('Access-Control-Allow-Headers', allowed);
}

return headers;
}

export default async function cors(req: Request, res: Response, options?: CorsOptions) {
const opts = { ...defaultOptions, ...options };
const { headers } = res;
const originHeaders = await originHeadersFromReq(req, opts.origin ?? false);
const mergeHeaders = (v: string, k: string) => {
if (k === 'Vary') headers.append(k, v);
else headers.set(k, v);
};

// If there's no origin we won't touch the response
if (!originHeaders) return res;

originHeaders.forEach(mergeHeaders);

if (opts.credentials) {
headers.set('Access-Control-Allow-Credentials', 'true');
}

const exposed = Array.isArray(opts.exposedHeaders)
? opts.exposedHeaders.join(',')
: opts.exposedHeaders;

if (exposed) {
headers.set('Access-Control-Expose-Headers', exposed);
}

// Handle the preflight request
if (req.method === 'OPTIONS') {
if (opts.methods) {
const methods = Array.isArray(opts.methods) ? opts.methods.join(',') : opts.methods;

headers.set('Access-Control-Allow-Methods', methods);
}

getAllowedHeaders(req, opts.allowedHeaders).forEach(mergeHeaders);

if (typeof opts.maxAge === 'number') {
headers.set('Access-Control-Max-Age', String(opts.maxAge));
}

if (opts.preflightContinue) return res;

headers.set('Content-Length', '0');
return new Response(null, { headers, status: opts.optionsSuccessStatus });
}

// If we got here, it's a normal request
return res;
}

export function initCors(options?: CorsOptions) {
return (req: Request, res: Response) => cors(req, res, options);
}
35 changes: 35 additions & 0 deletions lib/genSSML.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export type StyleName =
| 'affectionate'
| 'angry'
| 'calm'
| 'cheerful'
| 'disgruntled'
| 'embarrassed'
| 'fearful'
| 'general'
| 'gentle'
| 'sad'
| 'serious';

export interface SsmlOptions {
name: string;
pitch?: number;
rate?: number;
style?: StyleName;
}

export const genSSML = (text: string, options: SsmlOptions) => {
const { name, style, rate, pitch } = options;
const ssml = [
'<speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" version="1.0" xml:lang="en-US">',
`<voice name="${name}">`,
style && `<mstts:express-as style="${style}">`,
rate && pitch && `<prosody rate="${rate * 100}%" pitch="${pitch * 100}%">`,
text,
rate && pitch && `</prosody>`,
style && `</mstts:express-as>`,
`</voice>`,
'</speak>',
];
return ssml.filter(Boolean).join('');
};
44 changes: 44 additions & 0 deletions lib/postMicrosoftSpeech.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { v4 as uuidv4 } from 'uuid';

import { type SsmlOptions, genSSML } from './genSSML';

const API =
'https://southeastasia.api.speech.microsoft.com/accfreetrial/texttospeech/acc/v3.0-beta1/vcg/speak';

export const postMicrosoftSpeech = (text: string, options: SsmlOptions): [any, any] => {
const data = JSON.stringify({
offsetInPlainText: 0,
properties: {
SpeakTriggerSource: 'AccTuningPagePlayButton',
},
ssml: genSSML(text, options),
ttsAudioFormat: 'audio-24khz-160kbitrate-mono-mp3',
});

const DEFAULT_HEADERS = {
'accept': '*/*',
'accept-language': 'zh-CN,zh;q=0.9',
'authority': 'southeastasia.api.speech.microsoft.com',
'content-type': 'application/json',
'customvoiceconnectionid': uuidv4(),
'origin': 'https://speech.microsoft.com',
'sec-ch-ua': '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-site',
'user-agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36',
};

return [
API,
{
body: data,
headers: DEFAULT_HEADERS,
method: 'POST',
responseType: 'arraybuffer',
},
];
};

0 comments on commit 680649c

Please sign in to comment.