feat: updates

This commit is contained in:
qier222 2022-10-28 20:29:04 +08:00
parent a1b0bcf4d3
commit 884f3df41a
No known key found for this signature in database
198 changed files with 4572 additions and 5336 deletions

65
packages/server/.gitignore vendored Normal file
View file

@ -0,0 +1,65 @@
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
# 0x
profile-*
# mac files
.DS_Store
# vim swap files
*.swp
# webstorm
.idea
# vscode
.vscode
*code-workspace
# clinic
profile*
*clinic*
*flamegraph*
# generated code
examples/typescript-server.js
test/types/index.js
# compiled app
dist

4
packages/server/.taprc Normal file
View file

@ -0,0 +1,4 @@
test-env: [
TS_NODE_FILES=true,
TS_NODE_PROJECT=./test/tsconfig.json
]

23
packages/server/README.md Normal file
View file

@ -0,0 +1,23 @@
# Getting Started with [Fastify-CLI](https://www.npmjs.com/package/fastify-cli)
This project was bootstrapped with Fastify-CLI.
## Available Scripts
In the project directory, you can run:
### `npm run dev`
To start the app in dev mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
### `npm start`
For production mode
### `npm run test`
Run the test cases.
## Learn More
To learn Fastify, check out the [Fastify documentation](https://www.fastify.io/docs/latest/).

41
packages/server/fly.toml Normal file
View file

@ -0,0 +1,41 @@
# fly.toml file generated for ypm on 2022-09-06T00:57:21+08:00
app = "ypm"
kill_signal = "SIGINT"
kill_timeout = 5
processes = ["npm run start"]
[build]
builder = "heroku/buildpacks:20"
[env]
PORT = "8080"
[experimental]
allowed_public_ports = []
auto_rollback = true
[[services]]
http_checks = []
internal_port = 35530
processes = ["app"]
protocol = "tcp"
script_checks = []
[services.concurrency]
hard_limit = 25
soft_limit = 20
type = "connections"
[[services.ports]]
handlers = ["http"]
port = 80
[[services.ports]]
handlers = ["tls", "http"]
port = 443
[[services.tcp_checks]]
grace_period = "1s"
interval = "15s"
restart_limit = 0
timeout = "2s"

View file

@ -0,0 +1,37 @@
{
"name": "server",
"version": "1.0.0",
"description": "This project was bootstrapped with Fastify-CLI.",
"main": "app.ts",
"directories": {
"test": "test"
},
"scripts": {
"test": "npm run build:ts && tsc -p test/tsconfig.json && tap --ts \"test/**/*.test.ts\"",
"start": "fastify start --port 35530 --address 0.0.0.0 -l info dist/app.js",
"build:ts": "tsc",
"watch:ts": "tsc -w",
"dev": "npm run build:ts && concurrently -k -p \"[{name}]\" -n \"TypeScript,App\" -c \"yellow.bold,cyan.bold\" \"npm:watch:ts\" \"npm:dev:start\"",
"dev:start": "fastify start --ignore-watch=.ts$ -w --port 35530 --address 0.0.0.0 -l info -P dist/app.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@fastify/autoload": "^5.0.0",
"@fastify/sensible": "^4.1.0",
"axios": "^0.27.2",
"fastify": "^4.0.0",
"fastify-cli": "^4.4.0",
"fastify-plugin": "^3.0.0"
},
"devDependencies": {
"@types/node": "^18.0.0",
"@types/tap": "^15.0.5",
"concurrently": "^7.0.0",
"fastify-tsconfig": "^1.0.1",
"tap": "^16.1.0",
"ts-node": "^10.4.0",
"typescript": "^4.5.4"
}
}

View file

@ -0,0 +1,35 @@
import { join } from 'path';
import AutoLoad, {AutoloadPluginOptions} from '@fastify/autoload';
import { FastifyPluginAsync } from 'fastify';
export type AppOptions = {
// Place your custom options for app below here.
} & Partial<AutoloadPluginOptions>;
const app: FastifyPluginAsync<AppOptions> = async (
fastify,
opts
): Promise<void> => {
// Place here your custom code!
// Do not touch the following lines
// This loads all plugins defined in plugins
// those should be support plugins that are reused
// through your application
void fastify.register(AutoLoad, {
dir: join(__dirname, 'plugins'),
options: opts
})
// This loads all plugins defined in routes
// define your routes in one of these
void fastify.register(AutoLoad, {
dir: join(__dirname, 'routes'),
options: opts
})
};
export default app;
export { app }

View file

@ -0,0 +1,16 @@
# Plugins Folder
Plugins define behavior that is common to all the routes in your
application. Authentication, caching, templates, and all the other cross
cutting concerns should be handled by plugins placed in this folder.
Files in this folder are typically defined through the
[`fastify-plugin`](https://github.com/fastify/fastify-plugin) module,
making them non-encapsulated. They can define decorators and set hooks
that will then be used in the rest of your application.
Check out:
* [The hitchhiker's guide to plugins](https://www.fastify.io/docs/latest/Guides/Plugins-Guide/)
* [Fastify decorators](https://www.fastify.io/docs/latest/Reference/Decorators/).
* [Fastify lifecycle](https://www.fastify.io/docs/latest/Reference/Lifecycle/).

View file

@ -0,0 +1,13 @@
import fp from 'fastify-plugin'
import sensible, { SensibleOptions } from '@fastify/sensible'
/**
* This plugins adds some utilities to handle http errors
*
* @see https://github.com/fastify/fastify-sensible
*/
export default fp<SensibleOptions>(async (fastify, opts) => {
fastify.register(sensible, {
errorHandler: false
})
})

View file

@ -0,0 +1,20 @@
import fp from 'fastify-plugin'
export interface SupportPluginOptions {
// Specify Support plugin options here
}
// The use of fastify-plugin is required to be able
// to export the decorators to the outer scope
export default fp<SupportPluginOptions>(async (fastify, opts) => {
fastify.decorate('someSupport', function () {
return 'hugs'
})
})
// When using .decorate you have to specify added properties for Typescript
declare module 'fastify' {
export interface FastifyInstance {
someSupport(): string;
}
}

View file

@ -0,0 +1,38 @@
import { FastifyPluginAsync } from 'fastify'
import appleMusicRequest from '../../utils/appleMusicRequest'
const example: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
fastify.get<{
Querystring: {
name: string
artist: string
lang: 'zh-CN' | 'en-US'
}
}>('/album', async function (request, reply) {
const { name, lang, artist } = request.query
const fromApple = await appleMusicRequest({
method: 'GET',
url: '/search',
params: {
term: name,
types: 'albums',
'fields[albums]': 'artistName,name,editorialVideo,editorialNotes',
limit: '1',
l: lang.toLowerCase(),
},
})
const albums = fromApple?.results?.album?.data
const album =
albums?.find(
(a: any) =>
a.attributes.name.toLowerCase() === name.toLowerCase() &&
a.attributes.artistName.toLowerCase() === artist.toLowerCase()
) || albums?.[0]
return album
})
}
export default example

View file

@ -0,0 +1,46 @@
import { FastifyPluginAsync } from 'fastify'
import appleMusicRequest from '../../utils/appleMusicRequest'
const example: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
fastify.get<{
Querystring: {
name: string
lang: 'zh-CN' | 'en-US'
}
}>('/artist', async function (request, reply) {
const { name, lang } = request.query
if (!name) {
return {
code: 400,
message: 'params "name" is required',
}
}
const fromApple = await appleMusicRequest({
method: 'GET',
url: '/search',
params: {
term: name,
types: 'artists',
'fields[artists]': 'url,name,artwork,editorialVideo,artistBio',
'omit[resource:artists]': 'relationships',
platform: 'web',
limit: '1',
l: lang?.toLowerCase() || 'en-us',
with: 'serverBubbles',
},
})
const artist = fromApple?.results?.artist?.data?.[0]
if (
artist &&
artist?.attributes?.name?.toLowerCase() === name.toLowerCase()
) {
return artist
}
})
}
export default example

View file

@ -0,0 +1,9 @@
import { FastifyPluginAsync } from 'fastify'
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
fastify.get('/', async function (request, reply) {
return { root: true }
})
}
export default root;

View file

@ -0,0 +1,57 @@
import axios, {
AxiosError,
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
} from 'axios'
export const baseURL = 'https://amp-api.music.apple.com/v1/catalog/us'
export const headers = {
Authority: 'amp-api.music.apple.com',
Accept: '*/*',
Authorization:
'Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IldlYlBsYXlLaWQifQ.eyJpc3MiOiJBTVBXZWJQbGF5IiwiaWF0IjoxNjYxNDQwNDMyLCJleHAiOjE2NzY5OTI0MzIsInJvb3RfaHR0cHNfb3JpZ2luIjpbImFwcGxlLmNvbSJdfQ.z4BMv9_O4MpMK2iFhYkDqPsx53soPSnlXXK3jm99pHqGOrZADvTgEUw2U7_B1W0MAtFiWBYhYcGvWrzaOig6Bw',
Referer: 'https://music.apple.com/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'cross-site',
'User-Agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Cider/1.5.1 Chrome/100.0.4896.160 Electron/18.3.3 Safari/537.36',
'Accept-Encoding': 'gzip',
Origin: 'https://music.apple.com',
}
export const params = {
platform: 'web',
with: 'serverBubbles',
}
const service: AxiosInstance = axios.create({
baseURL,
withCredentials: true,
timeout: 15000,
})
service.interceptors.request.use((config: AxiosRequestConfig) => {
config.headers = { ...headers, ...config.headers }
config.params = { ...params, ...config.params }
return config
})
service.interceptors.response.use(
(response: AxiosResponse) => {
const res = response
return res
},
(error: AxiosError) => {
return Promise.reject(error)
}
)
const appleMusicRequest = async (config: AxiosRequestConfig) => {
const { data } = await service.request(config)
return data as any
}
export default appleMusicRequest

View file

@ -0,0 +1,35 @@
// This file contains code that we reuse between our tests.
import Fastify from 'fastify'
import fp from 'fastify-plugin'
import App from '../src/app'
import * as tap from 'tap';
export type Test = typeof tap['Test']['prototype'];
// Fill in this config with all the configurations
// needed for testing the application
async function config () {
return {}
}
// Automatically build and tear down our instance
async function build (t: Test) {
const app = Fastify()
// fastify-plugin ensures that all decorators
// are exposed for testing purposes, this is
// different from the production setup
void app.register(fp(App), await config())
await app.ready();
// Tear down our app after we are done
t.teardown(() => void app.close())
return app
}
export {
config,
build
}

View file

@ -0,0 +1,11 @@
import { test } from 'tap'
import Fastify from 'fastify'
import Support from '../../src/plugins/support'
test('support works standalone', async (t) => {
const fastify = Fastify()
void fastify.register(Support)
await fastify.ready()
t.equal(fastify.someSupport(), 'hugs')
})

View file

@ -0,0 +1,12 @@
import { test } from 'tap'
import { build } from '../helper'
test('example is loaded', async (t) => {
const app = await build(t)
const res = await app.inject({
url: '/example'
})
t.equal(res.payload, 'this is an example')
})

View file

@ -0,0 +1,11 @@
import { test } from 'tap'
import { build } from '../helper'
test('default root route', async (t) => {
const app = await build(t)
const res = await app.inject({
url: '/'
})
t.same(JSON.parse(res.payload), { root: true })
})

View file

@ -0,0 +1,8 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"noEmit": true
},
"include": ["../src/**/*.ts", "**/*.ts"]
}

View file

@ -0,0 +1,8 @@
{
"extends": "fastify-tsconfig",
"compilerOptions": {
"outDir": "dist",
"sourceMap": true
},
"include": ["src/**/*.ts"]
}