parent
bb9a1bdfa6
commit
35cd904a63
32 changed files with 1512 additions and 1243 deletions
@ -0,0 +1,44 @@ |
||||
|
||||
// This interface describes a JSON format in which the "interesting part"
|
||||
// of the entire database for a user can be imported/exported.
|
||||
// Worth noting is that the IDs used in this format only exist for cross-
|
||||
// referencing between objects. They do not correspond to IDs in the actual
|
||||
// database.
|
||||
// Upon import, they might be replaced, and upon export, they might be randomly
|
||||
|
||||
import { AlbumWithRefsWithId, ArtistWithRefsWithId, isAlbumWithRefs, isArtistWithRefs, isTagWithRefs, isTrackBaseWithRefs, isTrackWithRefs, TagWithRefsWithId, TrackWithRefsWithId } from "../types/resources"; |
||||
|
||||
// generated.
|
||||
export interface DBImportExportFormat { |
||||
tracks: TrackWithRefsWithId[], |
||||
albums: AlbumWithRefsWithId[], |
||||
artists: ArtistWithRefsWithId[], |
||||
tags: TagWithRefsWithId[], |
||||
} |
||||
|
||||
// Get a full export of a user's database (GET).
|
||||
export const DBExportEndpoint = "/export"; |
||||
export type DBExportResponse = DBImportExportFormat; |
||||
|
||||
// Fully replace the user's database by an import (POST).
|
||||
export const DBImportEndpoint = "/import"; |
||||
export type DBImportRequest = DBImportExportFormat; |
||||
export type DBImportResponse = void; |
||||
export const checkDBImportRequest: (v: any) => boolean = (v: any) => { |
||||
return 'tracks' in v && |
||||
'albums' in v && |
||||
'artists' in v && |
||||
'tags' in v && |
||||
v.tracks.reduce((prev: boolean, cur: any) => { |
||||
return prev && isTrackWithRefs(cur); |
||||
}, true) && |
||||
v.albums.reduce((prev: boolean, cur: any) => { |
||||
return prev && isAlbumWithRefs(cur); |
||||
}, true) && |
||||
v.artists.reduce((prev: boolean, cur: any) => { |
||||
return prev && isArtistWithRefs(cur); |
||||
}, true) && |
||||
v.tags.reduce((prev: boolean, cur: any) => { |
||||
return prev && isTagWithRefs(cur); |
||||
}, true); |
||||
} |
@ -0,0 +1,65 @@ |
||||
import React, { useReducer, useState } from 'react'; |
||||
import { WindowState } from "../Windows"; |
||||
import { Box, Paper, Typography, TextField, Button } from "@material-ui/core"; |
||||
import SaveIcon from '@material-ui/icons/Save'; |
||||
import { Alert } from '@material-ui/lab'; |
||||
import * as serverApi from '../../../api/api'; |
||||
|
||||
export interface ManageImportExportWindowState extends WindowState { |
||||
dummy: boolean |
||||
} |
||||
export enum ManageImportExportWindowActions { |
||||
SetDummy = "SetDummy", |
||||
} |
||||
export function ManageImportExportWindowReducer(state: ManageImportExportWindowState, action: any) { |
||||
switch (action.type) { |
||||
case ManageImportExportWindowActions.SetDummy: { |
||||
return state; |
||||
} |
||||
default: |
||||
throw new Error("Unimplemented ManageImportExportWindow state update.") |
||||
} |
||||
} |
||||
|
||||
export default function ManageImportExportWindow(props: {}) { |
||||
const [state, dispatch] = useReducer(ManageImportExportWindowReducer, { |
||||
dummy: true, |
||||
}); |
||||
|
||||
return <ManageImportExportWindowControlled state={state} dispatch={dispatch} /> |
||||
} |
||||
|
||||
export function ManageImportExportWindowControlled(props: { |
||||
state: ManageImportExportWindowState, |
||||
dispatch: (action: any) => void, |
||||
}) { |
||||
return <> |
||||
<Box width="100%" justifyContent="center" display="flex" flexWrap="wrap"> |
||||
<Box |
||||
m={1} |
||||
mt={4} |
||||
width="80%" |
||||
> |
||||
<SaveIcon style={{ fontSize: 80 }} /> |
||||
</Box> |
||||
<Box |
||||
m={1} |
||||
mt={4} |
||||
width="80%" |
||||
> |
||||
<Typography variant="h4">Import / Export</Typography> |
||||
<Box mt={2} /> |
||||
<Typography> |
||||
An exported database contains all your artists, albums, tracks and tags.<br /> |
||||
It is represented as a JSON structure. |
||||
</Typography> |
||||
<Box mt={2} /> |
||||
<Alert severity="warning">Upon importing a previously exported database, your database will be completely replaced!</Alert> |
||||
<Box mt={2} /> |
||||
<a href={(process.env.REACT_APP_BACKEND || "") + serverApi.DBExportEndpoint}> |
||||
<Button variant="outlined">Export</Button> |
||||
</a> |
||||
</Box> |
||||
</Box> |
||||
</> |
||||
} |
@ -0,0 +1,11 @@ |
||||
const { createProxyMiddleware } = require('http-proxy-middleware'); |
||||
|
||||
module.exports = function(app) { |
||||
app.use( |
||||
process.env.REACT_APP_BACKEND, |
||||
createProxyMiddleware({ |
||||
target: 'http://localhost:5000', |
||||
changeOrigin: true, |
||||
}) |
||||
); |
||||
}; |
@ -0,0 +1,45 @@ |
||||
import Knex from "knex"; |
||||
import { exportDB, importDB } from "../db/ImportExport"; |
||||
import { EndpointError, EndpointHandler, handleErrorsInEndpoint } from "./types"; |
||||
import * as api from '../../client/src/api/api'; |
||||
import { DBExportEndpoint } from "../../client/src/api/api"; |
||||
|
||||
export const DBExport: EndpointHandler = async (req: any, res: any, knex: Knex) => { |
||||
try { |
||||
let db = await exportDB(req.user.id, knex); |
||||
res.status(200); |
||||
res.setHeader('Content-disposition', 'attachment; filename= mudbase.json'); |
||||
res.setHeader('Content-type', 'application/json'); |
||||
res.send( JSON.stringify(db, null, 2) ); |
||||
} catch (e) { |
||||
handleErrorsInEndpoint(e) |
||||
} |
||||
} |
||||
|
||||
export const DBImport: EndpointHandler = async (req: any, res: any, knex: Knex) => { |
||||
if (!api.checkDBImportRequest(req.body)) { |
||||
const e: EndpointError = { |
||||
name: "EndpointError", |
||||
message: 'Invalid DBImport request', |
||||
httpStatus: 400 |
||||
}; |
||||
throw e; |
||||
} |
||||
const reqObject: api.DBImportRequest = req.body; |
||||
const { id: userId } = req.user; |
||||
|
||||
console.log("User ", userId, ": Import DB "); |
||||
|
||||
try { |
||||
await importDB(userId, reqObject, knex) |
||||
res.status(200).send(); |
||||
|
||||
} catch (e) { |
||||
handleErrorsInEndpoint(e); |
||||
} |
||||
} |
||||
|
||||
export const importExportEndpoints: [ string, string, boolean, EndpointHandler ][] = [ |
||||
[ api.DBExportEndpoint, 'get', true, DBExport ], |
||||
[ api.DBImportEndpoint, 'post', true, DBImport ], |
||||
]; |
Loading…
Reference in new issue