Dwayne Harris
5 years ago
11 changed files with 392 additions and 117 deletions
-
4src/constants.ts
-
36src/lib/media.ts
-
114src/plugins/api/apps.ts
-
51src/plugins/api/authentication.ts
-
58src/plugins/api/groups.ts
-
4src/plugins/api/index.ts
-
143src/plugins/api/media.ts
-
51src/plugins/api/uploads.ts
-
15src/plugins/api/users.ts
-
8src/schemas.ts
-
25src/types/collections.ts
@ -0,0 +1,36 @@ |
|||
import { Container } from '@azure/cosmos' |
|||
import { BlockBlobURL, SharedKeyCredential, Aborter, ContainerSASPermissions, generateBlobSASQueryParameters, AnonymousCredential } from '@azure/storage-blob' |
|||
import moment from 'moment' |
|||
|
|||
import { MEDIA_PARTITION_KEY } from '../constants' |
|||
import { Media } from '../types/collections' |
|||
|
|||
export function generateSAS(permissions: string, expirationMinutes: number) { |
|||
const sharedKeyCredential = new SharedKeyCredential(process.env.BLOB_STORAGE_ACCOUNT!, process.env.BLOB_STORAGE_ACCOUNT_KEY!) |
|||
|
|||
return generateBlobSASQueryParameters({ |
|||
containerName: process.env.BLOB_STORAGE_CONTAINER!, |
|||
permissions: ContainerSASPermissions.parse(permissions).toString(), |
|||
startTime: new Date(), |
|||
expiryTime: moment().add(expirationMinutes, 'm').toDate(), |
|||
}, sharedKeyCredential).toString() |
|||
} |
|||
|
|||
export async function deleteMedia(name: string) { |
|||
const blockBlobURL = new BlockBlobURL( |
|||
`https://${process.env.BLOB_STORAGE_ACCOUNT!}.blob.core.windows.net/${process.env.BLOB_STORAGE_CONTAINER!}/${name}`, |
|||
BlockBlobURL.newPipeline(new SharedKeyCredential(process.env.BLOB_STORAGE_ACCOUNT!, process.env.BLOB_STORAGE_ACCOUNT_KEY!)) |
|||
) |
|||
|
|||
await blockBlobURL.delete(Aborter.none) |
|||
} |
|||
|
|||
export async function attachMedia(container: Container, name: string) { |
|||
const mediaItem = container.item(name, MEDIA_PARTITION_KEY) |
|||
const { resource: media } = await mediaItem.read<Media>() |
|||
|
|||
await mediaItem.replace<Media>({ |
|||
...media, |
|||
attached: true, |
|||
}) |
|||
} |
@ -0,0 +1,143 @@ |
|||
import { |
|||
FastifyInstance, |
|||
Plugin, |
|||
DefaultQuery, |
|||
DefaultParams, |
|||
DefaultHeaders, |
|||
DefaultBody, |
|||
RouteShorthandOptions, |
|||
} from 'fastify' |
|||
|
|||
import { Server, IncomingMessage, ServerResponse } from 'http' |
|||
|
|||
import { MEDIA_PARTITION_KEY } from '../../constants' |
|||
import { errorSchema } from '../../schemas' |
|||
import { containerFor, getItem } from '../../lib/database' |
|||
import { badRequestError, serverError } from '../../lib/errors' |
|||
import { deleteMedia, generateSAS } from '../../lib/media' |
|||
import { createId } from '../../lib/utils' |
|||
|
|||
import { Media } from '../../types/collections' |
|||
import { PluginOptions } from '../../types' |
|||
|
|||
function getSASRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) { |
|||
const options: RouteShorthandOptions = { |
|||
schema: { |
|||
response: { |
|||
200: { |
|||
type: 'object', |
|||
properties: { |
|||
sas: { type: 'string' }, |
|||
id: { type: 'string' }, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
server.get<DefaultQuery, DefaultParams, DefaultHeaders, DefaultBody>('/api/sas', options, async () => { |
|||
return { |
|||
sas: generateSAS('arcw', 5), |
|||
id: createId(), |
|||
} |
|||
}) |
|||
} |
|||
|
|||
function addRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) { |
|||
interface Body { |
|||
name: string |
|||
size: number |
|||
type: string |
|||
originalName: string |
|||
} |
|||
|
|||
const options: RouteShorthandOptions = { |
|||
schema: { |
|||
body: { |
|||
type: 'object', |
|||
required: ['name', 'size', 'type', 'originalName'], |
|||
properties: { |
|||
name: { type: 'string' }, |
|||
size: { type: 'number' }, |
|||
type: { type: 'string' }, |
|||
originalName: { type: 'string' }, |
|||
}, |
|||
}, |
|||
response: { |
|||
400: errorSchema, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
server.post<DefaultQuery, DefaultParams, DefaultHeaders, Body>('/api/media', options, async (request, reply) => { |
|||
if (!server.database) return serverError(reply) |
|||
|
|||
const { name, size, type, originalName } = request.body |
|||
|
|||
const container = containerFor(server.database.client, 'Media') |
|||
const item = await getItem<Media>({ |
|||
container, |
|||
id: name, |
|||
partitionKey: MEDIA_PARTITION_KEY, |
|||
}) |
|||
|
|||
reply.code(204) |
|||
|
|||
if (item) return |
|||
|
|||
await container.items.create<Media>({ |
|||
id: name, |
|||
pk: MEDIA_PARTITION_KEY, |
|||
size, |
|||
type, |
|||
originalName, |
|||
attached: false, |
|||
created: Date.now(), |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
function deleteRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) { |
|||
interface Body { |
|||
name: string |
|||
} |
|||
|
|||
const options: RouteShorthandOptions = { |
|||
schema: { |
|||
body: { |
|||
type: 'object', |
|||
required: ['name'], |
|||
properties: { |
|||
name: { type: 'string' }, |
|||
}, |
|||
}, |
|||
response: { |
|||
400: errorSchema, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
server.post<DefaultQuery, DefaultParams, DefaultHeaders, Body>('/api/media/delete', options, async (request, reply) => { |
|||
if (!server.database) return serverError(reply) |
|||
|
|||
const mediaItem = containerFor(server.database.client, 'Media').item(request.body.name, MEDIA_PARTITION_KEY) |
|||
const { resource: media } = await mediaItem.read<Media>() |
|||
|
|||
if (!media) return badRequestError(reply) |
|||
|
|||
reply.code(204) |
|||
|
|||
if (media.attached) return |
|||
|
|||
await mediaItem.delete() |
|||
await deleteMedia(request.body.name) |
|||
}) |
|||
} |
|||
|
|||
const plugin: Plugin<Server, IncomingMessage, ServerResponse, PluginOptions> = async server => { |
|||
getSASRoute(server) |
|||
addRoute(server) |
|||
deleteRoute(server) |
|||
} |
|||
|
|||
export default plugin |
@ -1,51 +0,0 @@ |
|||
import { |
|||
FastifyInstance, |
|||
Plugin, |
|||
DefaultQuery, |
|||
DefaultParams, |
|||
DefaultHeaders, |
|||
DefaultBody, |
|||
RouteShorthandOptions, |
|||
} from 'fastify' |
|||
|
|||
import { Server, IncomingMessage, ServerResponse } from 'http' |
|||
import moment from 'moment' |
|||
import { SharedKeyCredential, ContainerSASPermissions, generateBlobSASQueryParameters } from '@azure/storage-blob' |
|||
import { createId } from '../../lib/utils' |
|||
import { PluginOptions } from '../../types' |
|||
|
|||
function getSASRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) { |
|||
const options: RouteShorthandOptions = { |
|||
schema: { |
|||
response: { |
|||
200: { |
|||
type: 'object', |
|||
properties: { |
|||
sas: { type: 'string' }, |
|||
id: { type: 'string' }, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
server.get<DefaultQuery, DefaultParams, DefaultHeaders, DefaultBody>('/api/sas', options, async () => { |
|||
const sharedKeyCredential = new SharedKeyCredential(process.env.BLOB_STORAGE_ACCOUNT!, process.env.BLOB_STORAGE_ACCOUNT_KEY!) |
|||
|
|||
return { |
|||
sas: generateBlobSASQueryParameters({ |
|||
containerName: process.env.BLOB_STORAGE_CONTAINER!, |
|||
permissions: ContainerSASPermissions.parse('arcw').toString(), |
|||
startTime: new Date(), |
|||
expiryTime: moment().add(5, 'm').toDate(), |
|||
}, sharedKeyCredential).toString(), |
|||
id: createId(), |
|||
} |
|||
}) |
|||
} |
|||
|
|||
const plugin: Plugin<Server, IncomingMessage, ServerResponse, PluginOptions> = async server => { |
|||
getSASRoute(server) |
|||
} |
|||
|
|||
export default plugin |
Write
Preview
Loading…
Cancel
Save
Reference in new issue