Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Commit

Permalink
feat: add ucanto aggregation api service
Browse files Browse the repository at this point in the history
  • Loading branch information
vasco-santos committed May 17, 2023
1 parent 0c64f25 commit 7524d63
Show file tree
Hide file tree
Showing 15 changed files with 258 additions and 3 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"deploy": "sst deploy",
"remove": "sst remove",
"console": "sst console",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"clean": "rm -rf node_modules pnpm-lock.yml packages/*/{pnpm-lock.yml,.next,out,coverage,.nyc_output,worker,dist,node_modules}"
},
"devDependencies": {
"@types/git-rev-sync": "^2.0.0",
Expand Down
20 changes: 20 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@spade-proxy/core",
"version": "0.0.0",
"type": "module",
"scripts": {
"test": "sst bind vitest",
"typecheck": "tsc -noEmit"
},
"dependencies": {
"@ipld/dag-ucan": "3.3.2",
"@ucanto/interface": "7.1.0",
"@ucanto/principal": "^7.0.0"
},
"devDependencies": {
"@types/node": "^18.16.3",
"vitest": "^0.31.0",
"sst": "^2.8.3",
"typescript": "^5.0.4"
}
}
30 changes: 30 additions & 0 deletions packages/core/service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as ed25519 from '@ucanto/principal/ed25519'
import * as DID from '@ipld/dag-ucan/did'
import { Signer } from '@ucanto/interface'

export const createUcantoServer = (servicePrincipal: Signer, context: UcantoServerCtx) => {

}

/**
* Given a config, return a ucanto Signer object representing the service
*/
export function getServiceSigner(config: ServiceSignerCtx) {
const signer = ed25519.parse(config.PRIVATE_KEY)
if (config.SPADE_PROXY_DID) {
const did = DID.parse(config.SPADE_PROXY_DID).did()
return signer.withDID(did)
}
return signer
}

export interface UcantoServerCtx {

}

export type ServiceSignerCtx = {
// multiformats private key of primary signing key
PRIVATE_KEY: string
// public DID for the upload service (did:key:... derived from PRIVATE_KEY if not set)
SPADE_PROXY_DID: string
}
1 change: 1 addition & 0 deletions packages/core/sst-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference path="../../.sst/types/index.ts" />
6 changes: 6 additions & 0 deletions packages/core/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "@tsconfig/node16/tsconfig.json",
"compilerOptions": {
"module": "esnext"
}
}
3 changes: 3 additions & 0 deletions packages/functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"test": "sst bind vitest",
"typecheck": "tsc -noEmit"
},
"dependencies": {
"@spade-proxy/core": "*"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.115",
"@types/node": "^18.16.3",
Expand Down
47 changes: 47 additions & 0 deletions packages/functions/src/api/ucan-invocation-router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Config } from 'sst/node/config'
import { APIGatewayProxyHandlerV2, APIGatewayProxyEventV2 } from 'aws-lambda'

import {
createUcantoServer,
getServiceSigner
} from '@spade-proxy/core/service'
// TODO: sentry

async function ucanInvocationRouter(request: APIGatewayProxyEventV2) {
const {
OFFER_BUCKET_NAME: offerBucketName = '',
SPADE_PROXY_DID
} = process.env

if (!SPADE_PROXY_DID) {
return {
statusCode: 500,
}
} else if (request.body === undefined) {
return {
statusCode: 400,
}
}

const { PRIVATE_KEY } = Config
const serviceSigner = getServiceSigner({ SPADE_PROXY_DID, PRIVATE_KEY })
const server = await createUcantoServer(serviceSigner, {})

return {
statusCode: 200,
// TODO
}
}

export const handler: APIGatewayProxyHandlerV2 = async (event) => {
return ucanInvocationRouter(event)
}

// would be generated by sst, but requires `sst build` to be run, which calls out to aws; not great for CI
declare module 'sst/node/config' {
export interface SecretResources {
PRIVATE_KEY: {
value: string
}
}
}
13 changes: 13 additions & 0 deletions packages/functions/src/cron/merge-offer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import fetch from "node-fetch";

export async function main() {
const weather = await checkSFWeather();
console.log(weather.consolidated_weather[0]);
return {};
}

function checkSFWeather() {
return fetch("https://www.metaweather.com/api/location/2487956/").then(
(res) => res.json()
);
}
1 change: 1 addition & 0 deletions packages/functions/sst-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference path="../../.sst/types/index.ts" />
81 changes: 81 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion seed.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

before_compile:
- n 18.13.0
- npm ci
- pnpm i
4 changes: 4 additions & 0 deletions sst.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Tags } from 'aws-cdk-lib';
import { SSTConfig } from 'sst';
import { ApiStack } from './stacks/ApiStack';
import { CronStack } from './stacks/CronStack';
import { DataStack } from './stacks/DataStack';

export default {
config(_input) {
Expand All @@ -20,7 +22,9 @@ export default {
sourcemap: true
}
})
app.stack(DataStack);
app.stack(ApiStack);
app.stack(CronStack);

// tags let us discover all the aws resource costs incurred by this app
// see: https://docs.sst.dev/advanced/tagging-resources
Expand Down
16 changes: 15 additions & 1 deletion stacks/ApiStack.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
import {
Api,
Config,
StackContext,
use
} from 'sst/constructs';

import { DataStack } from './DataStack';
import { getApiPackageJson, getGitInfo } from './config';

export function ApiStack({ stack }: StackContext) {
const { offerBucket } = use(DataStack)

// Setup API
const customDomain = {
domainName: undefined
}
// const customDomain = getCustomDomain(stack.stage, process.env.HOSTED_ZONE)
const pkg = getApiPackageJson()
const git = getGitInfo()
const privateKey = new Config.Secret(stack, 'PRIVATE_KEY')

const api = new Api(stack, 'api', {
defaults: {
function: {
permissions: [
offerBucket
],
environment: {
OFFER_BUCKET_NAME: offerBucket.bucketName,
NAME: pkg.name,
VERSION: pkg.version,
COMMIT: git.commmit,
STAGE: stack.stage,
}
},
bind: [
privateKey
]
}
},
routes: {
'POST /': 'packages/functions/src/api/ucan-invocation-router.handler',
'GET /version': 'packages/functions/src/api/version.handler',
}
})
Expand Down
17 changes: 17 additions & 0 deletions stacks/CronStack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Cron, StackContext, use } from 'sst/constructs';
import { DataStack } from './DataStack';

export function CronStack({ stack }: StackContext) {
const { offerBucket } = use(DataStack)

// Get state
// Write finalized files
const mergeOfferCron = new Cron(stack, 'merge-offer-cron', {
schedule: 'rate(1 minute)',
job: 'packages/functions/src/cron/merge-offer.main',
});

mergeOfferCron.attachPermissions([
offerBucket
])
}
17 changes: 17 additions & 0 deletions stacks/DataStack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Bucket, StackContext } from 'sst/constructs';

import { getBucketConfig } from './config';

export function DataStack({ stack }: StackContext) {
const bucket = getBucketConfig('offer-store', stack.stage)
const offerBucket = new Bucket(stack, bucket.bucketName, {
cors: true,
cdk: {
bucket
}
})

return {
offerBucket
}
}

0 comments on commit 7524d63

Please sign in to comment.