@@ -1712,6 +1712,187 @@ alter function pg_catalog.lo_import(text, oid) owner to postgres;
1712
1712
},
1713
1713
},
1714
1714
},
1715
+ {
1716
+ ID : "service_functions" ,
1717
+ Name : "Functions" ,
1718
+ Type : schema .ServiceTypeDockerimage ,
1719
+ Builder : schema .ServiceBuilderDocker ,
1720
+ Image : utils .ToPtr ("supabase/edge-runtime:v1.67.4" ),
1721
+ DependsOn : []string {"service_postgresql" , "service_kong" },
1722
+ Ports : []schema.PortSpec {
1723
+ {
1724
+ Port : 9000 ,
1725
+ Protocol : utils .ToPtr (schema .ProtocolTCP ),
1726
+ },
1727
+ },
1728
+ RunCommand : utils .ToPtr ("edge-runtime start --main-service /home/deno/functions/main" ),
1729
+ VariablesMounts : []* schema.VariableMount {
1730
+ {
1731
+ Name : "main_index_ts" ,
1732
+ Path : "/home/deno/functions/main/index.ts" ,
1733
+ },
1734
+ {
1735
+ Name : "hello_index_ts" ,
1736
+ Path : "/home/deno/functions/hello/index.ts" ,
1737
+ },
1738
+ },
1739
+ VariableReferences : []schema.TemplateVariableReference {
1740
+ {
1741
+ SourceID : "service_postgresql" ,
1742
+ SourceName : "DATABASE_PASSWORD" ,
1743
+ TargetName : "POSTGRES_PASSWORD" ,
1744
+ },
1745
+ {
1746
+ SourceID : "service_kong" ,
1747
+ SourceName : "JWT_SECRET" ,
1748
+ TargetName : "JWT_SECRET" ,
1749
+ },
1750
+ {
1751
+ SourceID : "service_kong" ,
1752
+ SourceName : "SUPABASE_ANON_KEY" ,
1753
+ TargetName : "SUPABASE_ANON_KEY" ,
1754
+ },
1755
+ {
1756
+ SourceID : "service_kong" ,
1757
+ SourceName : "SUPABASE_SERVICE_KEY" ,
1758
+ TargetName : "SUPABASE_SERVICE_ROLE_KEY" ,
1759
+ },
1760
+ {
1761
+ SourceID : "service_kong" ,
1762
+ TargetName : "SUPABASE_URL" ,
1763
+ IsHost : true ,
1764
+ },
1765
+ {
1766
+ SourceID : "service_postgresql" ,
1767
+ SourceName : "DATABASE_PASSWORD" ,
1768
+ TargetName : "SUPABASE_DB_URL" ,
1769
+ AdditionalTemplateSources : []string {"DATABASE_HOST" },
1770
+ TemplateString : "postgresql://postgres:${DATABASE_PASSWORD}@${DATABASE_HOST}:5432/postgres" ,
1771
+ },
1772
+ },
1773
+ Variables : []schema.TemplateVariable {
1774
+ {
1775
+ Name : "VERIFY_JWT" ,
1776
+ Value : "false" ,
1777
+ },
1778
+ {
1779
+ Name : "main_index_ts" ,
1780
+ Value : `import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
1781
+ import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts'
1782
+
1783
+ console.log('main function started')
1784
+
1785
+ const JWT_SECRET = Deno.env.get('JWT_SECRET')
1786
+ const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true'
1787
+
1788
+ function getAuthToken(req: Request) {
1789
+ const authHeader = req.headers.get('authorization')
1790
+ if (!authHeader) {
1791
+ throw new Error('Missing authorization header')
1792
+ }
1793
+ const [bearer, token] = authHeader.split(' ')
1794
+ if (bearer !== 'Bearer') {
1795
+ throw new Error("Auth header is not 'Bearer {token}'")
1796
+ }
1797
+ return token
1798
+ }
1799
+
1800
+ async function verifyJWT(jwt: string): Promise<boolean> {
1801
+ const encoder = new TextEncoder()
1802
+ const secretKey = encoder.encode(JWT_SECRET)
1803
+ try {
1804
+ await jose.jwtVerify(jwt, secretKey)
1805
+ } catch (err) {
1806
+ console.error(err)
1807
+ return false
1808
+ }
1809
+ return true
1810
+ }
1811
+
1812
+ serve(async (req: Request) => {
1813
+ if (req.method !== 'OPTIONS' && VERIFY_JWT) {
1814
+ try {
1815
+ const token = getAuthToken(req)
1816
+ const isValidJWT = await verifyJWT(token)
1817
+
1818
+ if (!isValidJWT) {
1819
+ return new Response(JSON.stringify({ msg: 'Invalid JWT' }), {
1820
+ status: 401,
1821
+ headers: { 'Content-Type': 'application/json' },
1822
+ })
1823
+ }
1824
+ } catch (e) {
1825
+ console.error(e)
1826
+ return new Response(JSON.stringify({ msg: e.toString() }), {
1827
+ status: 401,
1828
+ headers: { 'Content-Type': 'application/json' },
1829
+ })
1830
+ }
1831
+ }
1832
+
1833
+ const url = new URL(req.url)
1834
+ const { pathname } = url
1835
+ const path_parts = pathname.split('/')
1836
+ const service_name = path_parts[1]
1837
+
1838
+ if (!service_name || service_name === '') {
1839
+ const error = { msg: 'missing function name in request' }
1840
+ return new Response(JSON.stringify(error), {
1841
+ status: 400,
1842
+ headers: { 'Content-Type': 'application/json' },
1843
+ })
1844
+ }
1845
+
1846
+ const servicePath = "/home/deno/functions/" + service_name
1847
+ console.error("serving the request with " + servicePath)
1848
+
1849
+ const memoryLimitMb = 150
1850
+ const workerTimeoutMs = 1 * 60 * 1000
1851
+ const noModuleCache = false
1852
+ const importMapPath = null
1853
+ const envVarsObj = Deno.env.toObject()
1854
+ const envVars = Object.keys(envVarsObj).map((k) => [k, envVarsObj[k]])
1855
+
1856
+ try {
1857
+ const worker = await EdgeRuntime.userWorkers.create({
1858
+ servicePath,
1859
+ memoryLimitMb,
1860
+ workerTimeoutMs,
1861
+ noModuleCache,
1862
+ importMapPath,
1863
+ envVars,
1864
+ })
1865
+ return await worker.fetch(req)
1866
+ } catch (e) {
1867
+ const error = { msg: e.toString() }
1868
+ return new Response(JSON.stringify(error), {
1869
+ status: 500,
1870
+ headers: { 'Content-Type': 'application/json' },
1871
+ })
1872
+ }
1873
+ })` ,
1874
+ },
1875
+ {
1876
+ Name : "hello_index_ts" ,
1877
+ Value : `// Follow this setup guide to integrate the Deno language server with your editor:
1878
+ // https://deno.land/manual/getting_started/setup_your_environment
1879
+ // This enables autocomplete, go to definition, etc.
1880
+
1881
+ import { serve } from "https://deno.land/std@0.177.1/http/server.ts"
1882
+
1883
+ serve(async () => {
1884
+ return new Response(
1885
+ '"Hello from Edge Functions!"',
1886
+ { headers: { "Content-Type": "application/json" } },
1887
+ )
1888
+ })
1889
+
1890
+ // To invoke:
1891
+ // curl 'http://localhost:<KONG_HTTP_PORT>/functions/v1/hello' \\
1892
+ // --header 'Authorization: Bearer <anon/service_role API key>'` ,
1893
+ },
1894
+ },
1895
+ },
1715
1896
{
1716
1897
ID : "service_kong" ,
1717
1898
Name : "Kong" ,
@@ -1865,6 +2046,15 @@ services:
1865
2046
- /storage/v1/
1866
2047
plugins:
1867
2048
- name: cors
2049
+ - name: functions-v1
2050
+ url: http://${SERVICE_FUNCTIONS_KUBE_NAME}.${NAMESPACE}:9000/
2051
+ routes:
2052
+ - name: functions-v1-all
2053
+ strip_path: true
2054
+ paths:
2055
+ - /functions/v1/
2056
+ plugins:
2057
+ - name: cors
1868
2058
- name: meta
1869
2059
url: http://${SERVICE_POSTGRES_META_KUBE_NAME}.${NAMESPACE}:8080/
1870
2060
routes:
0 commit comments