Merge branch 'board-render-api-endpoint' into onboard_gallery

This commit is contained in:
snwy 2024-03-26 14:38:45 -04:00
commit 3fc1ff93b7
5 changed files with 116 additions and 97 deletions

View file

@ -288,6 +288,13 @@ const nextConfig = {
},
{ key: 'Access-Control-Allow-Headers', value: 'Content-Type' }
]
},
{
source: '/api/board/svg/(.+)',
headers: [{
key: 'content-type',
value: 'image/svg+xml'
}]
}
]
}

View file

@ -0,0 +1,17 @@
// test me with: curl http://localhost:3000/api/board/svg/https%3A%2F%2Fgithub.com%2Fhackclub%2FOnBoard%2Fraw%2Fmain%2Fprojects%2F2_Switch_Keyboard%2Fgerber.zip/bottom
import { gerberToSvg } from "."
export default async function handler(req, res) {
const { board_url } = req.query
if (!board_url) {
return res.status(404).json({ status: 404, error: 'Must provide file' })
}
// ensure valid file url is included
const parsed_url = new URL(decodeURI(board_url))
if (!parsed_url) {
return res.status(404).json({ status: 404, error: 'Invalid file' })
}
const svg = await gerberToSvg(parsed_url)
return res.status(200).send(svg.bottom)
}

View file

@ -0,0 +1,75 @@
import JSZip from "jszip";
import { read, plot, renderLayers, renderBoard, stringifySvg } from '@tracespace/core'
import fs from 'fs'
export const gerberToSvg = async (gerberURL) => {
const data = await fetch(gerberURL).then((res) => res.arrayBuffer());
const files = [];
const zip = new JSZip();
const zippedData = await new Promise((resolve, _reject) => {
zip.loadAsync(data).then(resolve)
})
const allowedExtensions = [
'gbr', // gerber
'drl', // drillfile
'gko', // gerber board outline
'gbl', // gerber bottom layer
'gbp', // gerber bottom paste
'gbs', // gerber bottom solder mask
'gbo', // gerber bottom silk
'gtl', // gerber top layer
'gto', // gerber top silk
'gts' // gerber top soldermask
]
const unzipJobs = Object.entries(zippedData.files).map(async ([filename, file]) => {
const extension = filename.split('.').pop().toLowerCase();
if (allowedExtensions.includes(extension)) {
const filePath = `/tmp/${filename}`
await new Promise((resolve, _reject) => {
file.async('uint8array').then(function (fileData) {
fs.writeFileSync(filePath, fileData)
files.push(filePath)
resolve()
}
)})
}
})
await Promise.all(unzipJobs)
let readResult;
try {
readResult = await read(files)
} catch (e) {
console.error(e)
return {}
}
const plotResult = plot(readResult)
const renderLayersResult = renderLayers(plotResult)
const renderBoardResult = renderBoard(renderLayersResult)
return {
top: stringifySvg(renderBoardResult.top),
bottom: stringifySvg(renderBoardResult.bottom),
// all: stringifySvg(renderLayersResult)
}
}
export default async function handler(req, res) {
const { file, format } = req.query
if (!file) {
return res.status(400).json({ status: 400, error: 'Must provide file' })
}
// ensure valid file url is included
const url = new URL(decodeURI(file))
const svg = await GenerateSVG(url)
if (format == 'top') {
res.contentType('image/svg');
return res.status(200).send(svg.top)
}
if (format == 'json') return res.status(200).json(svg)
return res.status(200).json(svg)
}
// test me with: curl http://localhost:3000/api/board/svg/https%3A%2F%2Fgithub.com%2Fhackclub%2FOnBoard%2Fraw%2Fmain%2Fprojects%2F2_Switch_Keyboard%2Fgerber.zip

View file

@ -0,0 +1,17 @@
// test me with: curl http://localhost:3000/api/board/svg/https%3A%2F%2Fgithub.com%2Fhackclub%2FOnBoard%2Fraw%2Fmain%2Fprojects%2F2_Switch_Keyboard%2Fgerber.zip/top
import { gerberToSvg } from "."
export default async function handler(req, res) {
const { board_url } = req.query
if (!board_url) {
return res.status(404).json({ status: 404, error: 'Must provide file' })
}
// ensure valid file url is included
const parsed_url = new URL(decodeURI(board_url))
if (!parsed_url) {
return res.status(404).json({ status: 404, error: 'Invalid file' })
}
const svg = await gerberToSvg(parsed_url)
return res.status(200).send(svg.top)
}

View file

@ -1,97 +0,0 @@
import JSZip from "jszip";
import {read, plot, renderLayers, renderBoard, stringifySvg} from '@tracespace/core'
import fs from 'fs'
async function asyncLoadZip(data) {
return await new Promise((resolve, reject) => {
JSZip.loadAsync(data).then(function (zip) {
resolve(zip)
}, function (e) {
reject(e)
});
})
}
async function asyncUnzip (file) {
const zip = new JSZip();
return await new Promise((resolve, reject) => {
zip.file(file).async("uint8array").then(function (data) {
resolve(data)
}, function (e) {
reject(e)
});
})
}
export const GenerateSVG = async (zipFile) => {
//const zip = new JSZip();
const data = await fetch(zipFile)
.then((res) => res.arrayBuffer());
let files = [];
if(!fs.existsSync('gerber'))
fs.mkdirSync('gerber');
try {
const zip = await asyncLoadZip(data)
await new Promise(async (resolve, reject) => {
for(let filename of Object.keys(zip.files)) {
await (zip.files[filename].async('uint8array').then(function (fileData) {
filename = filename.replace(/\//g, '_');
const extension = filename.split('.').pop().toLowerCase();
if (extension === 'gbr' || // gerber
extension === 'drl' || // drillfile
extension === 'gko' || // gerber board outline
extension === 'gbl' || // gerber bottom layer
extension === 'gbp' || // gerber bottom paste
extension === 'gbs' || // gerber bottom solder mask
extension === 'gbo' || // gerber bottom silk
extension === 'gtl' || // gerber top layer
extension === 'gto' || // gerber top silk
extension === 'gts' // gerber top soldermask
) {
//console.log('gerber/' + filename)
files.push('gerber/' + filename);
if(!fs.existsSync('gerber/' + filename))
fs.writeFileSync('gerber/' + filename, fileData);
}
}));
//console.log(zip.files[filename])
}
resolve();
});
} catch (e) {
console.error(e)
if(fs.existsSync('gerber'))
fs.rmdirSync('gerber', { recursive: true });
return {}
}
//console.log(files)
let readResult;
try {
readResult = await read(files)
} catch (e) {
console.error(e)
if(fs.existsSync('gerber'))
fs.rmdirSync('gerber', { recursive: true });
return {}
}
const plotResult = plot(readResult)
const renderLayersResult = renderLayers(plotResult)
const renderBoardResult = renderBoard(renderLayersResult)
if(fs.existsSync('gerber'))
fs.rmdirSync('gerber', { recursive: true });
return {
top: stringifySvg(renderBoardResult.top),
bottom: stringifySvg(renderBoardResult.bottom),
}
}
export default async function handler(req, res) {
const { file } = req.query
if (!file) {
return res.status(400).json({ status: 400, error: 'Must provide file' })
}
const svg = await GenerateSVG(file)
return res.status(200).json(svg)
}